From c36a5e80ce366b6154c063e6621e7e8b58a3bad9 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 14 May 2014 15:24:02 +0000 Subject: [PATCH] fpspreadsheet: Building a number format list which allows to drop most of the restrictions of the previous versions like number of decimal places. New number format "nfCustom" which passes the format string directly to the file. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3044 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel2demo/excel2write.lpr | 287 ++++-- .../examples/excel5demo/excel5write.lpr | 226 +++-- .../examples/excel8demo/excel8read.lpi | 12 +- .../examples/excel8demo/excel8read.lpr | 4 +- .../examples/excel8demo/excel8write.lpr | 212 +++-- .../examples/fpsgrid/fpsgrid.lpi | 184 ++-- .../examples/fpsgrid/mainform.lfm | 68 +- .../examples/fpsgrid/mainform.lrs | 862 +++++++++--------- components/fpspreadsheet/fpspreadsheet.pas | 538 +++++++++-- components/fpspreadsheet/fpsutils.pas | 679 ++++++++++++++ components/fpspreadsheet/xlsbiff2.pas | 262 ++++-- components/fpspreadsheet/xlsbiff5.pas | 170 ++-- components/fpspreadsheet/xlsbiff8.pas | 195 ++-- components/fpspreadsheet/xlscommon.pas | 579 ++++++------ 14 files changed, 2832 insertions(+), 1446 deletions(-) diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr index 27dba658f..46a740151 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr @@ -20,6 +20,8 @@ var number: Double; lCol: TCol; lRow: TRow; + r: Integer; + fmt: String; begin // Open the output file MyDir := ExtractFilePath(ParamStr(0)); @@ -34,7 +36,7 @@ begin MyWorksheet.WriteRowHeight(0, 30); // 30 mm // Turn off grid lines and hide headers - MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines, soShowHeaders]; + //MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines, soShowHeaders]; { -- currently not working //MyWorksheet.Options := MyWorksheet.Options + [soHasFrozenPanes]; @@ -108,67 +110,240 @@ begin MyWorksheet.WriteNumber(6, 3, 2017); MyWorksheet.WriteFont(6, 3, 'Arial', 18, [fssBold], scBlue); - // Write current date/time to cells B11:B16 - MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate'); - MyWorksheet.WriteDateTime(10, 1, now, nfShortDate); - MyWorksheet.WriteUTF8Text(11, 0, 'nfShortTime'); - MyWorksheet.WriteDateTime(11, 1, now, nfShortTime); - MyWorksheet.WriteUTF8Text(12, 0, 'nfLongTime'); - MyWorksheet.WriteDateTime(12, 1, now, nfLongTime); - MyWorksheet.WriteUTF8Text(13, 0, 'nfShortDateTime'); - MyWorksheet.WriteDateTime(13, 1, now, nfShortDateTime); - MyWorksheet.WriteUTF8Text(14, 0, 'nfFmtDateTime, DM'); - MyWorksheet.WriteDateTime(14, 1, now, nfFmtDateTime, 'DM'); - MyWorksheet.WriteUTF8Text(15, 0, 'nfFmtDateTime, MY'); - MyWorksheet.WriteDateTime(15, 1, now, nfFmtDateTime, 'MY'); - MyWorksheet.WriteUTF8Text(16, 0, 'nfShortTimeAM'); - MyWorksheet.WriteDateTime(16, 1, now, nfShortTimeAM); - MyWorksheet.WriteUTF8Text(17, 0, 'nfLongTimeAM'); - MyWorksheet.WriteDateTime(17, 1, now, nfLongTimeAM); - MyWorksheet.WriteUTF8Text(18, 0, 'nfFmtDateTime, MS'); - MyWorksheet.WriteDateTime(18, 1, now, nfFmtDateTime, 'MS'); - MyWorksheet.WriteUTF8Text(19, 0, 'nfFmtDateTime, MSZ'); - MyWorksheet.WriteDateTime(19, 1, now, nfFmtDateTime, 'MSZ'); + r:= 10; + // Write current date/time and test numbers for various formatting options + + MyWorksheet.WriteUTF8Text(r, 1, 'Formats in gray cells are not supported by BIFF2'); + + inc(r, 2); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDateTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDateTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, DM'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'DM'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MY'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MY'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MS'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MS'); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MSZ'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MSZ'); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz'); + MyWorksheet.WriteFontColor(r, 1, scGray); // Write formatted numbers number := 12345.67890123456789; - MyWorksheet.WriteUTF8Text(24, 1, '12345.67890123456789'); - MyWorksheet.WriteUTF8Text(24, 2, '-12345.67890123456789'); - MyWorksheet.WriteUTF8Text(25, 0, 'nfFixed, 0 decs'); - MyWorksheet.WriteNumber(25, 1, number, nfFixed, 0); - MyWorksheet.WriteNumber(25, 2, -number, nfFixed, 0); - MyWorksheet.WriteUTF8Text(26, 0, 'nfFixed, 2 decs'); - MyWorksheet.WriteNumber(26, 1, number, nfFixed, 2); - MyWorksheet.WriteNumber(26, 2, -number, nfFixed, 2); - MyWorksheet.WriteUTF8Text(27, 0, 'nfFixedTh, 0 decs'); - MyWorksheet.WriteNumber(27, 1, number, nfFixedTh, 0); - MyWorksheet.WriteNumber(27, 2, -number, nfFixedTh, 0); - MyWorksheet.WriteUTF8Text(28, 0, 'nfFixedTh, 2 decs'); - MyWorksheet.WriteNumber(28, 1, number, nfFixedTh, 2); - MyWorksheet.WriteNumber(28, 2, -number, nfFixedTh, 2); - MyWorksheet.WriteUTF8Text(29, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(29, 1, number, nfSci); - MyWorksheet.WriteNumber(29, 2, -number, nfSci); - MyWorksheet.WriteNumber(29, 3, 1.0/number, nfSci); - MyWorksheet.WriteNumber(29, 4, -1.0/number, nfSci); - MyWorksheet.WriteUTF8Text(30, 0, 'nfExp, 2 decs'); - MyWorksheet.WriteNumber(30, 1, number, nfExp, 2); - MyWorksheet.WriteNumber(30, 2, -number, nfExp, 2); - MyWorksheet.WriteNumber(30, 3, 1.0/number, nfExp, 2); - MyWorksheet.WriteNumber(30, 4, -1.0/number, nfExp, 2); - + inc(r, 2); + MyWorksheet.WriteUTF8Text(r, 1, '12345.67890123456789'); + MyWorksheet.WriteUTF8Text(r, 2, '-12345.67890123456789'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfGeneral'); + MyWorksheet.WriteNumber(r, 1, number, nfGeneral); + MyWorksheet.WriteNumber(r, 2, -number, nfGeneral); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 1); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 1); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 3); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 3); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 1); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 1); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); + MyWorksheet.WriteFontColor(r, 2, scGray); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); + MyWorksheet.WriteFontColor(r, 3, scGray); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); + MyWorksheet.WriteFontColor(r, 4, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); + MyWorksheet.WriteFontColor(r, 2, scGray); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); + MyWorksheet.WriteFontColor(r, 3, scGray); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); + MyWorksheet.WriteFontColor(r, 4, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); + MyWorksheet.WriteFontColor(r, 2, scGray); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); + MyWorksheet.WriteFontColor(r, 3, scGray); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); + MyWorksheet.WriteFontColor(r, 4, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 1); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 1); + MyWorksheet.WriteFontColor(r, 2, scGray); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 1); + MyWorksheet.WriteFontColor(r, 3, scGray); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 1); + MyWorksheet.WriteFontColor(r, 4, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 2); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 2); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 3); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 3); + MyWorksheet.WriteFontColor(r, 2, scGray); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 3); + MyWorksheet.WriteFontColor(r, 3, scGray); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3); + MyWorksheet.WriteFontColor(r, 4, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0_);("$"#,##0)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0_);("$"#,##0)'); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + fmt := '"€"#,##0.0_);[Red]("€"#,##0.0)'; + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, '+fmt); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + fmt := '[Green]"¥"#,##0.0_);[Red]-"¥"#,##0.0'; + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, '+fmt); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, _("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteFontColor(r, 1, scGray); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + MyWorksheet.WriteFontColor(r, 2, scGray); + inc(r, 2); number := 1.333333333; - MyWorksheet.WriteUTF8Text(35, 0, 'nfPercentage, 0 decs'); - MyWorksheet.WriteNumber(35, 1, number, nfPercentage, 0); - MyWorksheet.WriteUTF8Text(36, 0, 'nfPercentage, 2 decs'); - MyWorksheet.WriteNumber(36, 1, number, nfPercentage, 2); - MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval'); - MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 1); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 3); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm:ss'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m:s'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'H:M:s'); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:mm'); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m'); + MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h'); + MyWorksheet.WriteFontColor(r, 1, scGray); - // Set width of columns 0 and 1 - MyWorksheet.WriteColWidth(0, 40); - lCol.Width := 35; + // Set width of columns 0 to 3 + MyWorksheet.WriteColWidth(0, 50); + lCol.Width := 15; MyWorksheet.WriteColInfo(1, lCol); + MyWorksheet.WriteColInfo(2, lCol); + MyWorksheet.WriteColInfo(3, lCol); // Set height of rows 5 and 6 lRow.Height := 10; diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr index 7c3945b13..991bdbf42 100644 --- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr +++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr @@ -25,8 +25,9 @@ var MyWorksheet: TsWorksheet; MyRPNFormula: TsRPNFormula; MyDir: string; - i: Integer; + i, r: Integer; number: Double; + fmt: string; begin MyDir := ExtractFilePath(ParamStr(0)); @@ -50,6 +51,8 @@ begin MyWorksheet.WriteColWidth(0, 40); MyWorksheet.WriteColWidth(1, 20); MyWorksheet.WriteColWidth(2, 20); + MyWorksheet.WriteColWidth(3, 15); + MyWorksheet.WriteColWidth(4, 15); // Write some cells MyWorksheet.WriteNumber(0, 0, 1.0);// A1 @@ -155,62 +158,181 @@ begin MyRPNFormula[1].ElementKind := fekABS; MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula); + r:= 10; // Write current date/time to cells B11:B16 - MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate'); - MyWorksheet.WriteDateTime(10, 1, now, nfShortDate); - MyWorksheet.WriteUTF8Text(11, 0, 'nfShortTime'); - MyWorksheet.WriteDateTime(11, 1, now, nfShortTime); - MyWorksheet.WriteUTF8Text(12, 0, 'nfLongTime'); - MyWorksheet.WriteDateTime(12, 1, now, nfLongTime); - MyWorksheet.WriteUTF8Text(13, 0, 'nfShortDateTime'); - MyWorksheet.WriteDateTime(13, 1, now, nfShortDateTime); - MyWorksheet.WriteUTF8Text(14, 0, 'nfFmtDateTime, DM'); - MyWorksheet.WriteDateTime(14, 1, now, nfFmtDateTime, 'DM'); - MyWorksheet.WriteUTF8Text(15, 0, 'nfFmtDateTime, MY'); - MyWorksheet.WriteDateTime(15, 1, now, nfFmtDateTime, 'MY'); - MyWorksheet.WriteUTF8Text(16, 0, 'nfShortTimeAM'); - MyWorksheet.WriteDateTime(16, 1, now, nfShortTimeAM); - MyWorksheet.WriteUTF8Text(17, 0, 'nfLongTimeAM'); - MyWorksheet.WriteDateTime(17, 1, now, nfLongTimeAM); - MyWorksheet.WriteUTF8Text(18, 0, 'nfFmtDateTime, MS'); - MyWorksheet.WriteDateTime(18, 1, now, nfFmtDateTime, 'MS'); - MyWorksheet.WriteUTF8Text(19, 0, 'nfFmtDateTime, MSZ'); - MyWorksheet.WriteDateTime(19, 1, now, nfFmtDateTime, 'MSZ'); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDateTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDateTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, DM'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'DM'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MY'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MY'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MS'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MS'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MSZ'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MSZ'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz'); // Write formatted numbers number := 12345.67890123456789; - MyWorksheet.WriteUTF8Text(24, 1, '12345.67890123456789'); - MyWorksheet.WriteUTF8Text(24, 2, '-12345.67890123456789'); - MyWorksheet.WriteUTF8Text(25, 0, 'nfFixed, 0 decs'); - MyWorksheet.WriteNumber(25, 1, number, nfFixed, 0); - MyWorksheet.WriteNumber(25, 2, -number, nfFixed, 0); - MyWorksheet.WriteUTF8Text(26, 0, 'nfFixed, 2 decs'); - MyWorksheet.WriteNumber(26, 1, number, nfFixed, 2); - MyWorksheet.WriteNumber(26, 2, -number, nfFixed, 2); - MyWorksheet.WriteUTF8Text(27, 0, 'nfFixedTh, 0 decs'); - MyWorksheet.WriteNumber(27, 1, number, nfFixedTh, 0); - MyWorksheet.WriteNumber(27, 2, -number, nfFixedTh, 0); - MyWorksheet.WriteUTF8Text(28, 0, 'nfFixedTh, 2 decs'); - MyWorksheet.WriteNumber(28, 1, number, nfFixedTh, 2); - MyWorksheet.WriteNumber(28, 2, -number, nfFixedTh, 2); - MyWorksheet.WriteUTF8Text(29, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(29, 1, number, nfSci); - MyWorksheet.WriteNumber(29, 2, -number, nfSci); - MyWorksheet.WriteNumber(29, 3, 1.0/number, nfSci); - MyWorksheet.WriteNumber(29, 4, -1.0/number, nfSci); - MyWorksheet.WriteUTF8Text(30, 0, 'nfExp, 2 decs'); - MyWorksheet.WriteNumber(30, 1, number, nfExp, 2); - MyWorksheet.WriteNumber(30, 2, -number, nfExp, 2); - MyWorksheet.WriteNumber(30, 3, 1.0/number, nfExp, 2); - MyWorksheet.WriteNumber(30, 4, -1.0/number, nfExp, 2); - + inc(r, 2); + MyWorksheet.WriteUTF8Text(r, 1, '12345.67890123456789'); + MyWorksheet.WriteUTF8Text(r, 2, '-12345.67890123456789'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfGeneral'); + MyWorksheet.WriteNumber(r, 1, number, nfGeneral); + MyWorksheet.WriteNumber(r, 2, -number, nfGeneral); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 1); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 1); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 2); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 2); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 3); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 3); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0_);("$"#,##0)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0_);("$"#,##0)'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + inc(r); + fmt := '"€"#,##0.0_);[Red]("€"#,##0.0)'; + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, '+fmt); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, UTF8ToAnsi(fmt)); + inc(r); + fmt := '[Green]"¥"#,##0.0_);[Red]-"¥"#,##0.0'; + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, '+fmt); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, UTF8ToAnsi(fmt)); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, UTF8ToAnsi(fmt)); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, _("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + MyWorksheet.WriteNumber(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + MyWorksheet.WriteNumber(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)'); + inc(r, 2); number := 1.333333333; - MyWorksheet.WriteUTF8Text(35, 0, 'nfPercentage, 0 decs'); - MyWorksheet.WriteNumber(35, 1, number, nfPercentage, 0); - MyWorksheet.WriteUTF8Text(36, 0, 'nfPercentage, 2 decs'); - MyWorksheet.WriteNumber(36, 1, number, nfPercentage, 2); - MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval'); - MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm:ss'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m:s'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'H:M:s'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:mm'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h'); //MyFormula.FormulaStr := ''; diff --git a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi index 6dea16dd6..557786999 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8read.lpi +++ b/components/fpspreadsheet/examples/excel8demo/excel8read.lpi @@ -34,12 +34,17 @@ - + + + + + + @@ -53,6 +58,11 @@ + + + + + diff --git a/components/fpspreadsheet/examples/excel8demo/excel8read.lpr b/components/fpspreadsheet/examples/excel8demo/excel8read.lpr index 543528dbc..61228d310 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8read.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8read.lpr @@ -11,7 +11,7 @@ program excel8read; uses Classes, SysUtils, fpspreadsheet, xlsbiff8, - laz_fpspreadsheet; + laz_fpspreadsheet, fpsutils; var MyWorkbook: TsWorkbook; @@ -21,6 +21,8 @@ var i: Integer; CurCell: PCell; +{$R *.res} + begin // Open the input file MyDir := ExtractFilePath(ParamStr(0)); diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index 6d4fcc1cd..037d1905a 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -26,11 +26,10 @@ var MyWorksheet: TsWorksheet; MyRPNFormula: TsRPNFormula; MyDir: string; - i: Integer; - lCell: PCell; number: Double; + lCell: PCell; lCol: TCol; - lRow: TRow; + i, r: Integer; begin MyDir := ExtractFilePath(ParamStr(0)); @@ -41,7 +40,6 @@ begin MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet1); MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines]; - MyWorksheet.Options := MyWorksheet.Options + [soHasFrozenPanes]; myWorksheet.LeftPaneWidth := 1; MyWorksheet.TopPaneHeight := 2; @@ -129,7 +127,7 @@ begin // Uncomment this to test large XLS files - for i := 40 to 1000 do + for i := 50 to 1000 do begin // MyWorksheet.WriteUTF8Text(i, 0, ParamStr(0)); // MyWorksheet.WriteUTF8Text(i, 1, ParamStr(0)); @@ -158,67 +156,169 @@ begin MyRPNFormula[1].ElementKind := fekABS; MyWorksheet.WriteRPNFormula(0, 5, MyRPNFormula); + r:= 10; // Write current date/time to cells B11:B16 - MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate'); - MyWorksheet.WriteDateTime(10, 1, now, nfShortDate); - MyWorksheet.WriteUTF8Text(11, 0, 'nfShortTime'); - MyWorksheet.WriteDateTime(11, 1, now, nfShortTime); - MyWorksheet.WriteUTF8Text(12, 0, 'nfLongTime'); - MyWorksheet.WriteDateTime(12, 1, now, nfLongTime); - MyWorksheet.WriteUTF8Text(13, 0, 'nfShortDateTime'); - MyWorksheet.WriteDateTime(13, 1, now, nfShortDateTime); - MyWorksheet.WriteUTF8Text(14, 0, 'nfFmtDateTime, DM'); - MyWorksheet.WriteDateTime(14, 1, now, nfFmtDateTime, 'DM'); - MyWorksheet.WriteUTF8Text(15, 0, 'nfFmtDateTime, MY'); - MyWorksheet.WriteDateTime(15, 1, now, nfFmtDateTime, 'MY'); - MyWorksheet.WriteUTF8Text(16, 0, 'nfShortTimeAM'); - MyWorksheet.WriteDateTime(16, 1, now, nfShortTimeAM); - MyWorksheet.WriteUTF8Text(17, 0, 'nfLongTimeAM'); - MyWorksheet.WriteDateTime(17, 1, now, nfLongTimeAM); - MyWorksheet.WriteUTF8Text(18, 0, 'nfFmtDateTime, MS'); - MyWorksheet.WriteDateTime(18, 1, now, nfFmtDateTime, 'MS'); - MyWorksheet.WriteUTF8Text(19, 0, 'nfFmtDateTime, MSZ'); - MyWorksheet.WriteDateTime(19, 1, now, nfFmtDateTime, 'MSZ'); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongDate'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongDate); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDateTime'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortDateTime); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, DM'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'DM'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MY'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MY'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfShortTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTimeAM'); + MyWorksheet.WriteDateTime(r, 1, now, nfLongTimeAM); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MS'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MS'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MSZ'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MSZ'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz'); + MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz'); // Write formatted numbers number := 12345.67890123456789; - MyWorksheet.WriteUTF8Text(24, 1, '12345.67890123456789'); - MyWorksheet.WriteUTF8Text(24, 2, '-12345.67890123456789'); - MyWorksheet.WriteUTF8Text(25, 0, 'nfFixed, 0 decs'); - MyWorksheet.WriteNumber(25, 1, number, nfFixed, 0); - MyWorksheet.WriteNumber(25, 2, -number, nfFixed, 0); - MyWorksheet.WriteUTF8Text(26, 0, 'nfFixed, 2 decs'); - MyWorksheet.WriteNumber(26, 1, number, nfFixed, 2); - MyWorksheet.WriteNumber(26, 2, -number, nfFixed, 2); - MyWorksheet.WriteUTF8Text(27, 0, 'nfFixedTh, 0 decs'); - MyWorksheet.WriteNumber(27, 1, number, nfFixedTh, 0); - MyWorksheet.WriteNumber(27, 2, -number, nfFixedTh, 0); - MyWorksheet.WriteUTF8Text(28, 0, 'nfFixedTh, 2 decs'); - MyWorksheet.WriteNumber(28, 1, number, nfFixedTh, 2); - MyWorksheet.WriteNumber(28, 2, -number, nfFixedTh, 2); - MyWorksheet.WriteUTF8Text(29, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(29, 1, number, nfSci); - MyWorksheet.WriteNumber(29, 2, -number, nfSci); - MyWorksheet.WriteNumber(29, 3, 1.0/number, nfSci); - MyWorksheet.WriteNumber(29, 4, -1.0/number, nfSci); - MyWorksheet.WriteUTF8Text(30, 0, 'nfExp, 2 decs'); - MyWorksheet.WriteNumber(30, 1, number, nfExp, 2); - MyWorksheet.WriteNumber(30, 2, -number, nfExp, 2); - MyWorksheet.WriteNumber(30, 3, 1.0/number, nfExp, 2); - MyWorksheet.WriteNumber(30, 4, -1.0/number, nfExp, 2); - + inc(r, 2); + MyWorksheet.WriteUTF8Text(r, 1, '12345.67890123456789'); + MyWorksheet.WriteUTF8Text(r, 2, '-12345.67890123456789'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfGeneral'); + MyWorksheet.WriteNumber(r, 1, number, nfGeneral); + MyWorksheet.WriteNumber(r, 2, -number, nfGeneral); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixed, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixed, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfFixed, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 0); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 1); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 1); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 1); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 2); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 2); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 2); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfExp, 3); + MyWorksheet.WriteNumber(r, 2, -number, nfExp, 3); + MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 3); + MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "EUR "#,##0_);("EUR "#,##0)'); + MyWorksheet.WriteDateTime(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"EUR "#,##0_);("EUR "#,##0)'); + MyWorksheet.WriteDateTime(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"EUR "#,##0_);("EUR "#,##0)'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteDateTime(r, 1, number); + MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + MyWorksheet.WriteDateTime(r, 2, -number); + MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)'); + inc(r, 2); number := 1.333333333; - MyWorksheet.WriteUTF8Text(35, 0, 'nfPercentage, 0 decs'); - MyWorksheet.WriteNumber(35, 1, number, nfPercentage, 0); - MyWorksheet.WriteUTF8Text(36, 0, 'nfPercentage, 2 decs'); - MyWorksheet.WriteNumber(36, 1, number, nfPercentage, 2); - MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval'); - MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 0); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 1 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 1); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 2 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 2); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 3 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 3); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm:ss'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m:s'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'H:M:s'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:mm'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h'); + MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h'); // Set width of columns 0, 1 and 5 MyWorksheet.WriteColWidth(0, 25); lCol.Width := 20; MyWorksheet.WriteColInfo(1, lCol); + MyWorksheet.WriteColInfo(2, lCol); + MyWorksheet.WriteColWidth(3, 15); + MyWorksheet.WriteColWidth(4, 15); lCol.Width := 5; MyWorksheet.WriteColInfo(5, lCol); diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index ccd169e5d..8cd0a41f0 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -126,6 +126,7 @@ + @@ -137,25 +138,24 @@ - + - - + + + + + - - + - - + + - - - @@ -224,20 +224,20 @@ - - + - - + + + @@ -249,12 +249,10 @@ - - @@ -266,11 +264,11 @@ - + - - - + + + @@ -291,31 +289,31 @@ - + - - - + + + - + - - - + + + - + - - - + + + @@ -398,12 +396,10 @@ - - @@ -565,144 +561,136 @@ - - - - - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.lfm b/components/fpspreadsheet/examples/fpsgrid/mainform.lfm index 3a75fecc5..7f8f2d2db 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.lfm +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.lfm @@ -1,11 +1,11 @@ object Form1: TForm1 - Left = 370 - Height = 406 - Top = 258 - Width = 636 + Left = 359 + Height = 593 + Top = 193 + Width = 722 Caption = 'fpsGrid' - ClientHeight = 386 - ClientWidth = 636 + ClientHeight = 568 + ClientWidth = 722 Menu = MainMenu1 OnActivate = FormActivate OnCreate = FormCreate @@ -13,19 +13,19 @@ object Form1: TForm1 LCLVersion = '1.3' object Panel1: TPanel Left = 0 - Height = 73 - Top = 313 - Width = 636 + Height = 76 + Top = 492 + Width = 722 Align = alBottom BevelOuter = bvNone - ClientHeight = 73 - ClientWidth = 636 + ClientHeight = 76 + ClientWidth = 722 TabOrder = 0 object CbShowHeaders: TCheckBox Left = 8 - Height = 19 + Height = 24 Top = 11 - Width = 93 + Width = 116 Caption = 'Show headers' Checked = True OnClick = CbShowHeadersClick @@ -34,9 +34,9 @@ object Form1: TForm1 end object CbShowGridLines: TCheckBox Left = 8 - Height = 19 + Height = 24 Top = 36 - Width = 100 + Width = 125 Caption = 'Show grid lines' Checked = True OnClick = CbShowGridLinesClick @@ -45,7 +45,7 @@ object Form1: TForm1 end object EdFrozenCols: TSpinEdit Left = 238 - Height = 23 + Height = 28 Top = 8 Width = 52 OnChange = EdFrozenColsChange @@ -53,7 +53,7 @@ object Form1: TForm1 end object EdFrozenRows: TSpinEdit Left = 238 - Height = 23 + Height = 28 Top = 39 Width = 52 OnChange = EdFrozenRowsChange @@ -61,18 +61,18 @@ object Form1: TForm1 end object Label1: TLabel Left = 152 - Height = 15 + Height = 20 Top = 13 - Width = 62 + Width = 77 Caption = 'Frozen cols:' FocusControl = EdFrozenCols ParentColor = False end object Label2: TLabel Left = 153 - Height = 15 + Height = 20 Top = 40 - Width = 66 + Width = 82 Caption = 'Frozen rows:' FocusControl = EdFrozenRows ParentColor = False @@ -80,9 +80,9 @@ object Form1: TForm1 end object PageControl1: TPageControl Left = 0 - Height = 260 + Height = 439 Top = 53 - Width = 636 + Width = 722 ActivePage = TabSheet1 Align = alClient TabIndex = 0 @@ -90,13 +90,13 @@ object Form1: TForm1 OnChange = PageControl1Change object TabSheet1: TTabSheet Caption = 'Sheet1' - ClientHeight = 232 - ClientWidth = 628 + ClientHeight = 406 + ClientWidth = 714 object sWorksheetGrid1: TsWorksheetGrid Left = 0 - Height = 232 + Height = 406 Top = 0 - Width = 628 + Width = 714 FrozenCols = 0 FrozenRows = 0 Align = alClient @@ -108,7 +108,7 @@ object Form1: TForm1 TitleStyle = tsNative OnSelection = sWorksheetGrid1Selection ColWidths = ( - 42 + 56 64 ) end @@ -118,7 +118,7 @@ object Form1: TForm1 Left = 0 Height = 26 Top = 0 - Width = 636 + Width = 722 ButtonHeight = 24 Caption = 'ToolBar1' EdgeBorders = [] @@ -163,7 +163,7 @@ object Form1: TForm1 Left = 0 Height = 27 Top = 26 - Width = 636 + Width = 722 ButtonHeight = 23 Caption = 'FormatToolBar' Images = ImageList1 @@ -185,19 +185,19 @@ object Form1: TForm1 end object FontComboBox: TComboBox Left = 24 - Height = 23 + Height = 28 Top = 2 Width = 127 - ItemHeight = 15 + ItemHeight = 20 OnSelect = FontComboBoxSelect TabOrder = 0 end object FontSizeComboBox: TComboBox Left = 151 - Height = 23 + Height = 28 Top = 2 Width = 48 - ItemHeight = 15 + ItemHeight = 20 Items.Strings = ( '8' '9' diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.lrs b/components/fpspreadsheet/examples/fpsgrid/mainform.lrs index 9d61fffa2..02a826a19 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.lrs +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.lrs @@ -1,307 +1,308 @@ { This is an automatically generated lazarus resource file } LazarusResources.Add('TForm1','FORMDATA',[ - 'TPF0'#6'TForm1'#5'Form1'#4'Left'#3'r'#1#6'Height'#3#150#1#3'Top'#3#2#1#5'Wid' - +'th'#3'|'#2#7'Caption'#6#7'fpsGrid'#12'ClientHeight'#3#130#1#11'ClientWidth' - +#3'|'#2#4'Menu'#7#9'MainMenu1'#10'OnActivate'#7#12'FormActivate'#8'OnCreate' + 'TPF0'#6'TForm1'#5'Form1'#4'Left'#3'g'#1#6'Height'#3'Q'#2#3'Top'#3#193#0#5'Wi' + +'dth'#3#210#2#7'Caption'#6#7'fpsGrid'#12'ClientHeight'#3'8'#2#11'ClientWidth' + +#3#210#2#4'Menu'#7#9'MainMenu1'#10'OnActivate'#7#12'FormActivate'#8'OnCreate' +#7#10'FormCreate'#8'ShowHint'#9#10'LCLVersion'#6#3'1.3'#0#6'TPanel'#6'Panel1' - +#4'Left'#2#0#6'Height'#2'I'#3'Top'#3'9'#1#5'Width'#3'|'#2#5'Align'#7#8'alBot' - +'tom'#10'BevelOuter'#7#6'bvNone'#12'ClientHeight'#2'I'#11'ClientWidth'#3'|'#2 - +#8'TabOrder'#2#0#0#9'TCheckBox'#13'CbShowHeaders'#4'Left'#2#8#6'Height'#2#19 - +#3'Top'#2#11#5'Width'#2']'#7'Caption'#6#12'Show headers'#7'Checked'#9#7'OnCl' - +'ick'#7#18'CbShowHeadersClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#0#0#0#9 - +'TCheckBox'#15'CbShowGridLines'#4'Left'#2#8#6'Height'#2#19#3'Top'#2'$'#5'Wid' - +'th'#2'd'#7'Caption'#6#15'Show grid lines'#7'Checked'#9#7'OnClick'#7#20'CbSh' - +'owGridLinesClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#1#0#0#9'TSpinEdit' - +#12'EdFrozenCols'#4'Left'#3#238#0#6'Height'#2#23#3'Top'#2#8#5'Width'#2'4'#8 - +'OnChange'#7#18'EdFrozenColsChange'#8'TabOrder'#2#2#0#0#9'TSpinEdit'#12'EdFr' - +'ozenRows'#4'Left'#3#238#0#6'Height'#2#23#3'Top'#2''''#5'Width'#2'4'#8'OnCha' - +'nge'#7#18'EdFrozenRowsChange'#8'TabOrder'#2#3#0#0#6'TLabel'#6'Label1'#4'Lef' - +'t'#3#152#0#6'Height'#2#15#3'Top'#2#13#5'Width'#2'>'#7'Caption'#6#12'Frozen ' - +'cols:'#12'FocusControl'#7#12'EdFrozenCols'#11'ParentColor'#8#0#0#6'TLabel'#6 - +'Label2'#4'Left'#3#153#0#6'Height'#2#15#3'Top'#2'('#5'Width'#2'B'#7'Caption' - +#6#12'Frozen rows:'#12'FocusControl'#7#12'EdFrozenRows'#11'ParentColor'#8#0#0 - +#0#12'TPageControl'#12'PageControl1'#4'Left'#2#0#6'Height'#3#4#1#3'Top'#2'5' - +#5'Width'#3'|'#2#10'ActivePage'#7#9'TabSheet1'#5'Align'#7#8'alClient'#8'TabI' - +'ndex'#2#0#8'TabOrder'#2#1#8'OnChange'#7#18'PageControl1Change'#0#9'TTabShee' - +'t'#9'TabSheet1'#7'Caption'#6#6'Sheet1'#12'ClientHeight'#3#232#0#11'ClientWi' - +'dth'#3't'#2#0#15'TsWorksheetGrid'#15'sWorksheetGrid1'#4'Left'#2#0#6'Height' - +#3#232#0#3'Top'#2#0#5'Width'#3't'#2#10'FrozenCols'#2#0#10'FrozenRows'#2#0#5 - +'Align'#7#8'alClient'#8'ColCount'#2#2#14'ExtendedSelect'#8#7'Options'#11#15 - +'goFixedVertLine'#15'goFixedHorzLine'#10'goVertLine'#10'goHorzLine'#13'goRan' - +'geSelect'#11'goRowSizing'#11'goColSizing'#15'goThumbTracking'#14'goSmoothSc' - +'roll'#16'goFixedColSizing'#0#8'RowCount'#2#2#8'TabOrder'#2#0#10'TitleStyle' - +#7#8'tsNative'#11'OnSelection'#7#24'sWorksheetGrid1Selection'#9'ColWidths'#1 - +#2'*'#2'@'#0#0#0#0#0#8'TToolBar'#8'ToolBar1'#4'Left'#2#0#6'Height'#2#26#3'To' - +'p'#2#0#5'Width'#3'|'#2#12'ButtonHeight'#2#24#7'Caption'#6#8'ToolBar1'#11'Ed' - +'geBorders'#11#0#6'Images'#7#10'ImageList1'#8'TabOrder'#2#2#0#11'TToolButton' - +#11'ToolButton1'#4'Left'#2#1#3'Top'#2#0#6'Action'#7#6'AcOpen'#0#0#11'TToolBu' - +'tton'#11'ToolButton2'#4'Left'#2#24#3'Top'#2#0#6'Action'#7#8'AcSaveAs'#0#0#11 - +'TToolButton'#11'ToolButton3'#4'Left'#2'P'#3'Top'#2#0#6'Action'#7#6'AcQuit'#0 - +#0#11'TToolButton'#11'ToolButton5'#4'Left'#2'/'#3'Top'#2#0#5'Width'#2#5#7'Ca' - +'ption'#6#11'ToolButton5'#5'Style'#7#10'tbsDivider'#0#0#11'TToolButton'#11'T' - +'oolButton4'#4'Left'#2'4'#3'Top'#2#0#6'Action'#7#6'AcEdit'#0#0#11'TToolButto' - +'n'#11'ToolButton6'#4'Left'#2'K'#3'Top'#2#0#5'Width'#2#5#7'Caption'#6#11'Too' - +'lButton6'#5'Style'#7#10'tbsDivider'#0#0#0#8'TToolBar'#13'FormatToolBar'#4'L' - +'eft'#2#0#6'Height'#2#27#3'Top'#2#26#5'Width'#3'|'#2#12'ButtonHeight'#2#23#7 - +'Caption'#6#13'FormatToolBar'#6'Images'#7#10'ImageList1'#8'TabOrder'#2#3#0#11 - +'TToolButton'#12'ToolButton10'#4'Left'#3'('#1#3'Top'#2#2#6'Action'#7#11'AcLe' - +'ftAlign'#0#0#11'TToolButton'#12'ToolButton12'#4'Left'#3'?'#1#3'Top'#2#2#6'A' - +'ction'#7#16'AcHorCenterAlign'#0#0#11'TToolButton'#12'ToolButton13'#4'Left'#3 - +'V'#1#3'Top'#2#2#6'Action'#7#12'AcRightAlign'#0#0#9'TComboBox'#12'FontComboB' - +'ox'#4'Left'#2#24#6'Height'#2#23#3'Top'#2#2#5'Width'#2#127#10'ItemHeight'#2 - +#15#8'OnSelect'#7#18'FontComboBoxSelect'#8'TabOrder'#2#0#0#0#9'TComboBox'#16 - +'FontSizeComboBox'#4'Left'#3#151#0#6'Height'#2#23#3'Top'#2#2#5'Width'#2'0'#10 - +'ItemHeight'#2#15#13'Items.Strings'#1#6#1'8'#6#1'9'#6#2'10'#6#2'11'#6#2'12'#6 - +#2'14'#6#2'16'#6#2'18'#6#2'20'#6#2'24'#0#8'OnSelect'#7#22'FontSizeComboBoxSe' - +'lect'#8'TabOrder'#2#1#0#0#11'TToolButton'#11'ToolButton7'#4'Left'#3#199#0#3 - +'Top'#2#2#6'Action'#7#10'AcFontBold'#0#0#11'TToolButton'#11'ToolButton8'#4'L' - +'eft'#3#222#0#3'Top'#2#2#6'Action'#7#12'AcFontItalic'#0#0#11'TToolButton'#11 - +'ToolButton9'#4'Left'#3#245#0#3'Top'#2#2#6'Action'#7#15'AcFontUnderline'#0#0 - +#11'TToolButton'#12'ToolButton11'#4'Left'#3#12#1#3'Top'#2#2#6'Action'#7#15'A' - +'cFontStrikeout'#0#0#11'TToolButton'#12'ToolButton14'#4'Left'#3'#'#1#3'Top'#2 - +#2#5'Width'#2#5#7'Caption'#6#12'ToolButton14'#5'Style'#7#10'tbsDivider'#0#0 - +#11'TToolButton'#12'ToolButton15'#4'Left'#3'm'#1#3'Top'#2#2#5'Width'#2#5#7'C' - +'aption'#6#12'ToolButton15'#5'Style'#7#10'tbsDivider'#0#0#11'TToolButton'#12 - +'ToolButton16'#4'Left'#3'r'#1#3'Top'#2#2#6'Action'#7#11'AcVAlignTop'#0#0#11 - +'TToolButton'#12'ToolButton17'#4'Left'#3#137#1#3'Top'#2#2#6'Action'#7#14'AcV' - ,'AlignCenter'#0#0#11'TToolButton'#12'ToolButton18'#4'Left'#3#160#1#3'Top'#2#2 - +#6'Action'#7#14'AcVAlignBottom'#0#0#11'TToolButton'#12'ToolButton19'#4'Left' - +#3#183#1#3'Top'#2#2#5'Width'#2#5#7'Caption'#6#12'ToolButton19'#5'Style'#7#10 - +'tbsDivider'#0#0#11'TToolButton'#9'TbBorders'#4'Left'#3#188#1#3'Top'#2#2#6'A' - +'ction'#7#12'AcBorderNone'#12'DropdownMenu'#7#16'BordersPopupMenu'#5'Style'#7 - +#11'tbsDropDown'#0#0#9'TColorBox'#17'CbBackgroundColor'#4'Left'#3#223#1#6'He' - +'ight'#2#22#3'Top'#2#2#5'Width'#3#132#0#5'Style'#11#13'cbPrettyNames'#14'cbC' - +'ustomColors'#0#11'OnGetColors'#7#26'CbBackgroundColorGetColors'#10'ItemHeig' - +'ht'#2#16#8'OnSelect'#7#23'CbBackgroundColorSelect'#8'TabOrder'#2#2#0#0#11'T' - +'ToolButton'#12'ToolButton21'#4'Left'#2#1#3'Top'#2#2#6'Action'#7#6'AcFont'#0 - +#0#0#11'TOpenDialog'#11'OpenDialog1'#10'DefaultExt'#6#4'.xls'#6'Filter'#6#192 - +'Excel spreadsheet (*.xls)|*.xls|Excel XML spreadsheet (*.xlsx)|*.xlxs|Libre' - +'Office/OpenOffice spreadsheet (*.ods)|*.ods|Wikitable (pipes) (.wikitable_p' - +'ipes)|.wikitable_pipes|All files (*.*)|*.*'#7'Options'#11#20'ofExtensionDif' - +'ferent'#14'ofEnableSizing'#12'ofViewDetail'#0#4'left'#2'@'#3'top'#3#176#0#0 - +#0#11'TSaveDialog'#11'SaveDialog1'#10'DefaultExt'#6#4'.xls'#6'Filter'#6#185 - +'Excel spreadsheet (*.xls)|*.xls|Excel XML spreadsheet (*.xlsx)|*.xlsx|Libre' - +'Office/OpenOffice spreadsheet (*.ods)|*.ods|Wikitable (wikimedia) (.wikitab' - +'le_wikimedia)|*.wikitable_wikimedia'#7'Options'#11#17'ofOverwritePrompt'#20 - +'ofExtensionDifferent'#14'ofEnableSizing'#12'ofViewDetail'#0#4'left'#3#176#0 - +#3'top'#3#176#0#0#0#9'TMainMenu'#9'MainMenu1'#6'Images'#7#10'ImageList1'#4'l' - +'eft'#3' '#1#3'top'#2'@'#0#9'TMenuItem'#7'mnuFile'#7'Caption'#6#5'&File'#0#9 - +'TMenuItem'#7'mnuOpen'#6'Action'#7#6'AcOpen'#11'Bitmap.Data'#10':'#4#0#0'6'#4 - +#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0 - +#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0','#134#216#0'-'#136#216#247'-'#135 - +#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247'-'#136 - +#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247'-'#135#216#247'-'#136 - +#216#247','#134#216#0#255#255#255#0#255#255#255#0'3'#142#217#251#220#240#250 - +#255#152#225#246#255#149#224#246#255#146#223#246#255#142#222#245#255#137#220 - +#245#255#133#218#244#255#128#217#244#255'z'#215#243#255't'#213#243#255'p'#211 - +#242#255#194#234#248#255'5'#148#218#255#255#255#255#0#255#255#255#0'5'#148 - +#218#247#239#250#254#255#147#229#248#255#143#228#248#255#137#227#248#255#130 - +#225#247#255'z'#223#247#255'q'#222#246#255'g'#219#245#255'['#216#244#255'M' - +#212#243#255'@'#209#242#255#202#242#251#255'5'#148#218#255#255#255#255#0#255 - +#255#255#0'6'#154#218#248#242#250#253#255#148#230#248#255#146#229#248#255#144 - +#229#248#255#139#227#248#255#134#226#247#255#127#225#247#255'w'#222#246#255 - +'l'#220#246#255'^'#217#244#255'O'#213#243#255#204#242#251#255'5'#148#218#255 - +#255#255#255#0#255#255#255#0'6'#161#218#249#246#252#254#255#148#229#248#255 - +#147#229#248#255#147#229#248#255#145#229#248#255#147#219#233#255#147#215#227 - +#255#147#210#220#255#144#206#215#255#140#200#207#255#134#193#198#255#201#216 - +#214#255'5'#148#218#255#197'tD'#232#202#127'S'#241'7'#166#218#250#254#255#255 - +#255#248#253#255#255#246#253#255#255#245#252#255#255#243#252#254#255#154#228 - +#244#255#154#230#247#255#155#230#246#255#157#229#245#255#158#229#245#255#159 - +#229#244#255#218#243#248#255'5'#148#218#255#253#244#238#255#202#128'T'#249'5' - +#171#218#250#232#246#251#255'p'#188#231#255'U'#170#226#255'M'#165#224#255#145 - +#201#235#255#250#243#239#255#253#254#253#255#255#253#252#255#255#253#252#255 - +#254#253#252#255#254#252#251#255#254#254#253#255'5'#148#218#255#239#242#232 - +#255#206#129'V'#255'6'#170#218#242#241#250#253#255#148#222#245#255#147#220 - +#244#255'd'#188#233#255'5'#148#218#255'5'#148#218#255'5'#148#218#255'5'#148 - +#218#255'5'#148#218#255'5'#148#218#255'5'#148#218#255'5'#148#218#255'5'#148 - +#218#255#251#246#239#255#204#131'U'#254'5'#175#218#240#247#252#254#255#142 - +#228#248#255#145#222#245#255#159#224#245#255#172#225#246#255#202#132'R'#255 - +#255#247#241#255#255#233#217#255#255#234#219#255#255#233#217#255#255#231#215 - +#255#255#229#210#255#255#226#203#255#255#247#241#255#203#133'U'#254'6'#179 - +#218#248#253#254#254#255#254#255#255#255#254#254#255#255#253#254#255#255#254 - +#255#255#255#228#186#145#255#255#247#240#255#255#231#213#255#253#231#214#255 - +#253#230#212#255#252#228#208#255#251#227#203#255#250#220#194#255#254#243#232 - +#255#204#134'V'#254'4'#180#217#208'^'#194#225#250'`'#195#226#250'`'#195#226 - +#250'`'#195#226#250'_'#195#226#250#228#187#145#255#255#247#242#255#254#231 - +#213#255#254#231#213#255#253#229#209#255#250#224#202#255#249#222#196#255#247 - +#217#188#255#253#242#231#255#204#135'W'#254#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#228#187#146#255#254#247 - +#241#255#252#229#210#255#252#228#209#255#251#226#204#255#249#221#196#255#246 - +#215#187#255#243#209#175#255#250#239#228#255#204#135'X'#254#255#255#255#0#255 - ,#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#228#187 - +#146#255#254#246#240#255#252#226#205#255#252#227#205#255#250#223#200#255#247 - +#217#188#255#245#233#221#255#250#243#235#255#251#248#243#255#202#131'S'#254 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#228#187#147#255#254#245#237#255#252#222#197#255#251#224#199#255 - +#249#220#194#255#245#211#180#255#254#249#243#255#250#226#196#255#236#193#147 - +#255#195'}H'#147#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#229#190#150#255#255#255#254#255#253#243#233#255#253 - +#243#234#255#252#242#232#255#250#239#227#255#250#242#231#255#234#187#136#255 - +#207#133'U'#179#180'i='#12#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#234#195#157#255#230#191#150#255#228#187 - +#146#255#228#187#146#255#209#160'l'#245#208#158'm'#246#204#150'_'#218#196'yB' - +'~'#178'g<'#9#255#255#255#0#7'OnClick'#7#13'acOpenExecute'#0#0#9'TMenuItem'#9 - +'mnuSaveAs'#6'Action'#7#8'AcSaveAs'#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6' - +#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0 - +'d'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#186'j6'#0#185'i5'#181#184'i5'#238#183'h5' - +#255#181'h5'#255#180'g4'#255#178'f4'#255#176'e3'#255#174'd3'#255#172'c2'#255 - +#170'b2'#255#169'a2'#255#168'`1'#255#167'`1'#254#166'`1'#241#168'a1'#196#186 - +'j5'#222#235#198#173#255#234#197#173#255#254#251#248#255#254#251#248#255#254 - +#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255 - +#254#251#248#255#254#251#248#255#254#251#248#255#200#154'|'#255#199#152'y' - +#255#167'`1'#237#186'k7'#254#237#202#179#255#224#162'z'#255#254#250#247#255 - +'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255 - +'b'#192#136#255'b'#192#136#255'b'#192#136#255#253#249#246#255#202#141'e'#255 - +#201#155'|'#255#167'`1'#254#187'l8'#255#238#204#182#255#225#162'z'#255#254 - +#250#247#255#191#220#194#255#191#220#194#255#191#220#194#255#191#220#194#255 - +#191#220#194#255#191#220#194#255#191#220#194#255#191#220#194#255#253#249#246 - +#255#205#144'h'#255#204#158#129#255#168'a2'#255#187'k8'#255#239#206#184#255 - +#225#162'y'#255#254#250#247#255'b'#192#136#255'b'#192#136#255'b'#192#136#255 - +'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255 - +#253#249#246#255#207#147'j'#255#206#163#132#255#170'a2'#255#186'j6'#255#239 - +#208#187#255#226#162'z'#255#254#251#248#255#254#251#248#255#254#251#248#255 + +#4'Left'#2#0#6'Height'#2'L'#3'Top'#3#236#1#5'Width'#3#210#2#5'Align'#7#8'alB' + +'ottom'#10'BevelOuter'#7#6'bvNone'#12'ClientHeight'#2'L'#11'ClientWidth'#3 + +#210#2#8'TabOrder'#2#0#0#9'TCheckBox'#13'CbShowHeaders'#4'Left'#2#8#6'Height' + +#2#24#3'Top'#2#11#5'Width'#2't'#7'Caption'#6#12'Show headers'#7'Checked'#9#7 + +'OnClick'#7#18'CbShowHeadersClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#0#0 + +#0#9'TCheckBox'#15'CbShowGridLines'#4'Left'#2#8#6'Height'#2#24#3'Top'#2'$'#5 + +'Width'#2'}'#7'Caption'#6#15'Show grid lines'#7'Checked'#9#7'OnClick'#7#20'C' + +'bShowGridLinesClick'#5'State'#7#9'cbChecked'#8'TabOrder'#2#1#0#0#9'TSpinEdi' + +'t'#12'EdFrozenCols'#4'Left'#3#238#0#6'Height'#2#28#3'Top'#2#8#5'Width'#2'4' + +#8'OnChange'#7#18'EdFrozenColsChange'#8'TabOrder'#2#2#0#0#9'TSpinEdit'#12'Ed' + +'FrozenRows'#4'Left'#3#238#0#6'Height'#2#28#3'Top'#2''''#5'Width'#2'4'#8'OnC' + +'hange'#7#18'EdFrozenRowsChange'#8'TabOrder'#2#3#0#0#6'TLabel'#6'Label1'#4'L' + +'eft'#3#152#0#6'Height'#2#20#3'Top'#2#13#5'Width'#2'M'#7'Caption'#6#12'Froze' + +'n cols:'#12'FocusControl'#7#12'EdFrozenCols'#11'ParentColor'#8#0#0#6'TLabel' + +#6'Label2'#4'Left'#3#153#0#6'Height'#2#20#3'Top'#2'('#5'Width'#2'R'#7'Captio' + +'n'#6#12'Frozen rows:'#12'FocusControl'#7#12'EdFrozenRows'#11'ParentColor'#8 + +#0#0#0#12'TPageControl'#12'PageControl1'#4'Left'#2#0#6'Height'#3#183#1#3'Top' + +#2'5'#5'Width'#3#210#2#10'ActivePage'#7#9'TabSheet1'#5'Align'#7#8'alClient'#8 + +'TabIndex'#2#0#8'TabOrder'#2#1#8'OnChange'#7#18'PageControl1Change'#0#9'TTab' + +'Sheet'#9'TabSheet1'#7'Caption'#6#6'Sheet1'#12'ClientHeight'#3#150#1#11'Clie' + +'ntWidth'#3#202#2#0#15'TsWorksheetGrid'#15'sWorksheetGrid1'#4'Left'#2#0#6'He' + +'ight'#3#150#1#3'Top'#2#0#5'Width'#3#202#2#10'FrozenCols'#2#0#10'FrozenRows' + +#2#0#5'Align'#7#8'alClient'#8'ColCount'#2#2#14'ExtendedSelect'#8#7'Options' + +#11#15'goFixedVertLine'#15'goFixedHorzLine'#10'goVertLine'#10'goHorzLine'#13 + +'goRangeSelect'#11'goRowSizing'#11'goColSizing'#15'goThumbTracking'#14'goSmo' + +'othScroll'#16'goFixedColSizing'#0#8'RowCount'#2#2#8'TabOrder'#2#0#10'TitleS' + +'tyle'#7#8'tsNative'#11'OnSelection'#7#24'sWorksheetGrid1Selection'#9'ColWid' + +'ths'#1#2'8'#2'@'#0#0#0#0#0#8'TToolBar'#8'ToolBar1'#4'Left'#2#0#6'Height'#2 + +#26#3'Top'#2#0#5'Width'#3#210#2#12'ButtonHeight'#2#24#7'Caption'#6#8'ToolBar' + +'1'#11'EdgeBorders'#11#0#6'Images'#7#10'ImageList1'#8'TabOrder'#2#2#0#11'TTo' + +'olButton'#11'ToolButton1'#4'Left'#2#1#3'Top'#2#0#6'Action'#7#6'AcOpen'#0#0 + +#11'TToolButton'#11'ToolButton2'#4'Left'#2#24#3'Top'#2#0#6'Action'#7#8'AcSav' + +'eAs'#0#0#11'TToolButton'#11'ToolButton3'#4'Left'#2'P'#3'Top'#2#0#6'Action'#7 + +#6'AcQuit'#0#0#11'TToolButton'#11'ToolButton5'#4'Left'#2'/'#3'Top'#2#0#5'Wid' + +'th'#2#5#7'Caption'#6#11'ToolButton5'#5'Style'#7#10'tbsDivider'#0#0#11'TTool' + +'Button'#11'ToolButton4'#4'Left'#2'4'#3'Top'#2#0#6'Action'#7#6'AcEdit'#0#0#11 + +'TToolButton'#11'ToolButton6'#4'Left'#2'K'#3'Top'#2#0#5'Width'#2#5#7'Caption' + +#6#11'ToolButton6'#5'Style'#7#10'tbsDivider'#0#0#0#8'TToolBar'#13'FormatTool' + +'Bar'#4'Left'#2#0#6'Height'#2#27#3'Top'#2#26#5'Width'#3#210#2#12'ButtonHeigh' + +'t'#2#23#7'Caption'#6#13'FormatToolBar'#6'Images'#7#10'ImageList1'#8'TabOrde' + +'r'#2#3#0#11'TToolButton'#12'ToolButton10'#4'Left'#3'('#1#3'Top'#2#2#6'Actio' + +'n'#7#11'AcLeftAlign'#0#0#11'TToolButton'#12'ToolButton12'#4'Left'#3'?'#1#3 + +'Top'#2#2#6'Action'#7#16'AcHorCenterAlign'#0#0#11'TToolButton'#12'ToolButton' + +'13'#4'Left'#3'V'#1#3'Top'#2#2#6'Action'#7#12'AcRightAlign'#0#0#9'TComboBox' + +#12'FontComboBox'#4'Left'#2#24#6'Height'#2#28#3'Top'#2#2#5'Width'#2#127#10'I' + +'temHeight'#2#20#8'OnSelect'#7#18'FontComboBoxSelect'#8'TabOrder'#2#0#0#0#9 + +'TComboBox'#16'FontSizeComboBox'#4'Left'#3#151#0#6'Height'#2#28#3'Top'#2#2#5 + +'Width'#2'0'#10'ItemHeight'#2#20#13'Items.Strings'#1#6#1'8'#6#1'9'#6#2'10'#6 + +#2'11'#6#2'12'#6#2'14'#6#2'16'#6#2'18'#6#2'20'#6#2'24'#0#8'OnSelect'#7#22'Fo' + +'ntSizeComboBoxSelect'#8'TabOrder'#2#1#0#0#11'TToolButton'#11'ToolButton7'#4 + +'Left'#3#199#0#3'Top'#2#2#6'Action'#7#10'AcFontBold'#0#0#11'TToolButton'#11 + +'ToolButton8'#4'Left'#3#222#0#3'Top'#2#2#6'Action'#7#12'AcFontItalic'#0#0#11 + +'TToolButton'#11'ToolButton9'#4'Left'#3#245#0#3'Top'#2#2#6'Action'#7#15'AcFo' + +'ntUnderline'#0#0#11'TToolButton'#12'ToolButton11'#4'Left'#3#12#1#3'Top'#2#2 + +#6'Action'#7#15'AcFontStrikeout'#0#0#11'TToolButton'#12'ToolButton14'#4'Left' + +#3'#'#1#3'Top'#2#2#5'Width'#2#5#7'Caption'#6#12'ToolButton14'#5'Style'#7#10 + +'tbsDivider'#0#0#11'TToolButton'#12'ToolButton15'#4'Left'#3'm'#1#3'Top'#2#2#5 + +'Width'#2#5#7'Caption'#6#12'ToolButton15'#5'Style'#7#10'tbsDivider'#0#0#11'T' + +'ToolButton'#12'ToolButton16'#4'Left'#3'r'#1#3'Top'#2#2#6'Action'#7#11'AcVAl' + +'ignTop'#0#0#11'TToolButton'#12'ToolButton17'#4'Left'#3#137#1#3'Top'#2#2#6'A' + ,'ction'#7#14'AcVAlignCenter'#0#0#11'TToolButton'#12'ToolButton18'#4'Left'#3 + +#160#1#3'Top'#2#2#6'Action'#7#14'AcVAlignBottom'#0#0#11'TToolButton'#12'Tool' + +'Button19'#4'Left'#3#183#1#3'Top'#2#2#5'Width'#2#5#7'Caption'#6#12'ToolButto' + +'n19'#5'Style'#7#10'tbsDivider'#0#0#11'TToolButton'#9'TbBorders'#4'Left'#3 + +#188#1#3'Top'#2#2#6'Action'#7#12'AcBorderNone'#12'DropdownMenu'#7#16'Borders' + +'PopupMenu'#5'Style'#7#11'tbsDropDown'#0#0#9'TColorBox'#17'CbBackgroundColor' + +#4'Left'#3#223#1#6'Height'#2#22#3'Top'#2#2#5'Width'#3#132#0#5'Style'#11#13'c' + +'bPrettyNames'#14'cbCustomColors'#0#11'OnGetColors'#7#26'CbBackgroundColorGe' + +'tColors'#10'ItemHeight'#2#16#8'OnSelect'#7#23'CbBackgroundColorSelect'#8'Ta' + +'bOrder'#2#2#0#0#11'TToolButton'#12'ToolButton21'#4'Left'#2#1#3'Top'#2#2#6'A' + +'ction'#7#6'AcFont'#0#0#0#11'TOpenDialog'#11'OpenDialog1'#10'DefaultExt'#6#4 + +'.xls'#6'Filter'#6#192'Excel spreadsheet (*.xls)|*.xls|Excel XML spreadsheet' + +' (*.xlsx)|*.xlxs|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Wikitable' + +' (pipes) (.wikitable_pipes)|.wikitable_pipes|All files (*.*)|*.*'#7'Options' + +#11#20'ofExtensionDifferent'#14'ofEnableSizing'#12'ofViewDetail'#0#4'left'#2 + +'@'#3'top'#3#176#0#0#0#11'TSaveDialog'#11'SaveDialog1'#10'DefaultExt'#6#4'.x' + +'ls'#6'Filter'#6#185'Excel spreadsheet (*.xls)|*.xls|Excel XML spreadsheet (' + +'*.xlsx)|*.xlsx|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Wikitable (' + +'wikimedia) (.wikitable_wikimedia)|*.wikitable_wikimedia'#7'Options'#11#17'o' + +'fOverwritePrompt'#20'ofExtensionDifferent'#14'ofEnableSizing'#12'ofViewDeta' + +'il'#0#4'left'#3#176#0#3'top'#3#176#0#0#0#9'TMainMenu'#9'MainMenu1'#6'Images' + +#7#10'ImageList1'#4'left'#3' '#1#3'top'#2'@'#0#9'TMenuItem'#7'mnuFile'#7'Cap' + +'tion'#6#5'&File'#0#9'TMenuItem'#7'mnuOpen'#6'Action'#7#6'AcOpen'#11'Bitmap.' + +'Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16 + +#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0','#134#216 + +#0'-'#136#216#247'-'#135#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247 + +'-'#136#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247'-'#136#216#247 + +'-'#135#216#247'-'#136#216#247','#134#216#0#255#255#255#0#255#255#255#0'3' + +#142#217#251#220#240#250#255#152#225#246#255#149#224#246#255#146#223#246#255 + +#142#222#245#255#137#220#245#255#133#218#244#255#128#217#244#255'z'#215#243 + +#255't'#213#243#255'p'#211#242#255#194#234#248#255'5'#148#218#255#255#255#255 + +#0#255#255#255#0'5'#148#218#247#239#250#254#255#147#229#248#255#143#228#248 + +#255#137#227#248#255#130#225#247#255'z'#223#247#255'q'#222#246#255'g'#219#245 + +#255'['#216#244#255'M'#212#243#255'@'#209#242#255#202#242#251#255'5'#148#218 + +#255#255#255#255#0#255#255#255#0'6'#154#218#248#242#250#253#255#148#230#248 + +#255#146#229#248#255#144#229#248#255#139#227#248#255#134#226#247#255#127#225 + +#247#255'w'#222#246#255'l'#220#246#255'^'#217#244#255'O'#213#243#255#204#242 + +#251#255'5'#148#218#255#255#255#255#0#255#255#255#0'6'#161#218#249#246#252 + +#254#255#148#229#248#255#147#229#248#255#147#229#248#255#145#229#248#255#147 + +#219#233#255#147#215#227#255#147#210#220#255#144#206#215#255#140#200#207#255 + +#134#193#198#255#201#216#214#255'5'#148#218#255#197'tD'#232#202#127'S'#241'7' + +#166#218#250#254#255#255#255#248#253#255#255#246#253#255#255#245#252#255#255 + +#243#252#254#255#154#228#244#255#154#230#247#255#155#230#246#255#157#229#245 + +#255#158#229#245#255#159#229#244#255#218#243#248#255'5'#148#218#255#253#244 + +#238#255#202#128'T'#249'5'#171#218#250#232#246#251#255'p'#188#231#255'U'#170 + +#226#255'M'#165#224#255#145#201#235#255#250#243#239#255#253#254#253#255#255 + +#253#252#255#255#253#252#255#254#253#252#255#254#252#251#255#254#254#253#255 + +'5'#148#218#255#239#242#232#255#206#129'V'#255'6'#170#218#242#241#250#253#255 + +#148#222#245#255#147#220#244#255'd'#188#233#255'5'#148#218#255'5'#148#218#255 + +'5'#148#218#255'5'#148#218#255'5'#148#218#255'5'#148#218#255'5'#148#218#255 + +'5'#148#218#255'5'#148#218#255#251#246#239#255#204#131'U'#254'5'#175#218#240 + +#247#252#254#255#142#228#248#255#145#222#245#255#159#224#245#255#172#225#246 + +#255#202#132'R'#255#255#247#241#255#255#233#217#255#255#234#219#255#255#233 + +#217#255#255#231#215#255#255#229#210#255#255#226#203#255#255#247#241#255#203 + +#133'U'#254'6'#179#218#248#253#254#254#255#254#255#255#255#254#254#255#255 + +#253#254#255#255#254#255#255#255#228#186#145#255#255#247#240#255#255#231#213 + +#255#253#231#214#255#253#230#212#255#252#228#208#255#251#227#203#255#250#220 + +#194#255#254#243#232#255#204#134'V'#254'4'#180#217#208'^'#194#225#250'`'#195 + +#226#250'`'#195#226#250'`'#195#226#250'_'#195#226#250#228#187#145#255#255#247 + +#242#255#254#231#213#255#254#231#213#255#253#229#209#255#250#224#202#255#249 + +#222#196#255#247#217#188#255#253#242#231#255#204#135'W'#254#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#228#187 + +#146#255#254#247#241#255#252#229#210#255#252#228#209#255#251#226#204#255#249 + +#221#196#255#246#215#187#255#243#209#175#255#250#239#228#255#204#135'X'#254 + ,#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#228#187#146#255#254#246#240#255#252#226#205#255#252#227#205#255 + +#250#223#200#255#247#217#188#255#245#233#221#255#250#243#235#255#251#248#243 + +#255#202#131'S'#254#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#228#187#147#255#254#245#237#255#252#222#197#255 + +#251#224#199#255#249#220#194#255#245#211#180#255#254#249#243#255#250#226#196 + +#255#236#193#147#255#195'}H'#147#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#229#190#150#255#255#255#254#255 + +#253#243#233#255#253#243#234#255#252#242#232#255#250#239#227#255#250#242#231 + +#255#234#187#136#255#207#133'U'#179#180'i='#12#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#234#195#157#255#230 + +#191#150#255#228#187#146#255#228#187#146#255#209#160'l'#245#208#158'm'#246 + +#204#150'_'#218#196'yB~'#178'g<'#9#255#255#255#0#7'OnClick'#7#13'acOpenExecu' + +'te'#0#0#9'TMenuItem'#9'mnuSaveAs'#6'Action'#7#8'AcSaveAs'#11'Bitmap.Data'#10 + +':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0 + +' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#186'j6'#0#185'i5'#181 + +#184'i5'#238#183'h5'#255#181'h5'#255#180'g4'#255#178'f4'#255#176'e3'#255#174 + +'d3'#255#172'c2'#255#170'b2'#255#169'a2'#255#168'`1'#255#167'`1'#254#166'`1' + +#241#168'a1'#196#186'j5'#222#235#198#173#255#234#197#173#255#254#251#248#255 +#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248 - +#255#254#251#248#255#254#251#248#255#211#150'm'#255#210#167#138#255#171'b2' - +#255#187'j6'#255#240#210#190#255#226#163'z'#255#226#163'z'#255#225#163'z'#255 - +#226#163'{'#255#225#163'{'#255#224#161'x'#255#222#159'w'#255#221#159'v'#255 - +#220#157't'#255#217#155'r'#255#216#153'q'#255#214#153'p'#255#213#171#142#255 - +#173'c3'#255#187'j6'#255#242#213#194#255#227#163'z'#255#227#163'z'#255#226 - +#163'{'#255#226#163'{'#255#226#164'{'#255#225#162'y'#255#224#161'x'#255#222 - +#160'w'#255#222#158'u'#255#220#157't'#255#218#155's'#255#217#155's'#255#218 - +#176#149#255#175'd3'#255#187'j6'#255#242#216#197#255#227#164'{'#255#227#163 - +'z'#255#227#164'z'#255#226#164'{'#255#226#163'{'#255#225#163'{'#255#225#162 - +'y'#255#223#160'w'#255#222#159'v'#255#221#158't'#255#219#156'r'#255#220#157 - +'t'#255#221#181#154#255#177'e4'#255#187'k6'#255#244#217#199#255#230#166'}' - +#255#200#140'd'#255#201#141'e'#255#201#142'g'#255#203#146'l'#255#203#146'm' - +#255#202#144'i'#255#200#140'e'#255#200#140'd'#255#200#140'd'#255#200#140'd' - +#255#218#156't'#255#225#186#159#255#179'f4'#255#187'k6'#254#244#220#201#255 - +#231#167'}'#255#249#236#225#255#249#236#225#255#249#237#227#255#252#244#238 - +#255#253#250#247#255#253#247#243#255#250#237#229#255#247#231#219#255#247#229 - +#217#255#246#229#216#255#222#160'w'#255#228#190#164#255#180'g4'#255#188'k6' - +#250#245#221#204#255#231#168'~'#255#250#240#232#255#250#240#232#255#201#141 - +'f'#255#250#240#233#255#253#248#243#255#254#250#248#255#252#244#239#255#249 - +#233#223#255#247#231#219#255#247#229#217#255#224#162'x'#255#231#194#169#255 - +#182'h5'#255#188'k6'#240#246#223#208#255#232#168'~'#255#252#246#241#255#252 - +#246#241#255#200#140'd'#255#250#241#233#255#251#244#238#255#253#250#247#255 - +#253#249#246#255#250#240#232#255#248#232#221#255#247#230#219#255#225#163'z' - +#255#239#213#195#255#183'i5'#254#188'k6'#216#246#223#209#255#233#170#128#255 - +#254#250#246#255#253#250#246#255#200#140'd'#255#251#243#238#255#251#241#234 - +#255#252#246#242#255#254#251#248#255#252#246#241#255#249#236#226#255#248#231 - +#219#255#238#208#186#255#236#208#189#255#187'p>'#248#188'k6'#155#246#224#209 - +#255#247#224#209#255#254#251#248#255#254#251#247#255#253#249#246#255#252#245 - +#240#255#250#240#234#255#251#242#237#255#253#249#246#255#253#250#247#255#251 - +#241#235#255#248#233#223#254#236#208#189#251#201#137'^'#236#181'i5c'#188'k6q' - +#188'k6'#144#188'k6'#204#188'k6'#238#188'k6'#250#187'k6'#254#187'k6'#255#187 - ,'j6'#255#187'j6'#255#188'l9'#255#189'n;'#255#187'm:'#255#187'k8'#239#187'p>' - +#203#182'i5T'#255#255#255#0#7'OnClick'#7#15'acSaveAsExecute'#0#0#9'TMenuItem' - +#9'MenuItem1'#7'Caption'#6#1'-'#0#0#9'TMenuItem'#7'mnuQuit'#6'Action'#7#6'Ac' - +'Quit'#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0 - +#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0 - +#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#29'c'#155#22#25'`' - +#152'9'#20']'#149'b'#16'Z'#146#136#13'X'#144#164#19'\'#146#252#12'W'#143#237 - +#153#153#153#255'qqq'#255'TTT'#255'QQQ'#255'OOO'#255'LLL'#255'JJJ'#255'GGG' - +#255'EEE'#255'%g'#157#255'2t'#168#255'=|'#175#255'G'#132#181#255'N'#138#186 - +#255'>~'#173#255#12'W'#143#234#255#255#255#0#255#255#255#0'XXX'#255#162#162 - +#162#255#162#162#162#255#163#163#163#255#164#164#164#255#164#164#164#255#165 - +#165#165#255'/o'#165#255'x'#171#210#255'x'#171#211#255's'#167#209#255'i'#160 - +#205#255'@'#127#174#255#15'Y'#145#234#255#255#255#0#255#255#255#0'\\\'#255 - +#161#161#161#255''#255'4~;'#255'1y7'#255'.u4'#255'I' - +#145'P'#255'F'#143'L'#255'9s='#255#161#161#161#255#162#162#162#255'E~'#180 - +#255#136#183#217#255'g'#163#207#255'a'#158#204#255'c'#159#204#255'E'#131#177 - +#255#31'd'#156#234';'#135'B'#255#137#203#146#255#132#200#141#255#128#198#136 - +#255'{'#195#131#255'w'#193#127#255'G'#143'M'#255';t?'#255#161#161#161#255'L' - +#132#186#255#141#187#219#255'n'#168#209#255'f'#166#209#255'_'#180#223#255'G' - +#133#177#255'%i'#161#234'>'#139'F'#255#143#206#153#255'}'#198#135#255'x'#195 - +#129#255's'#192'|'#255't'#192'|'#255'y'#194#129#255'I'#144'O'#255'T'#127'W' - +#255'T'#137#191#255#148#191#221#255'u'#173#212#255'c'#184#225#255'K'#212#255 - +#255'B'#139#184#255',n'#166#234'A'#144'J'#255#148#210#159#255#145#208#154#255 - +#141#205#150#255#137#203#146#255#132#200#141#255'Q'#152'X'#255'A|F'#255#159 - +#159#159#255'Z'#142#196#255#152#195#224#255'|'#179#215#255't'#175#214#255'^' - +#196#237#255'K'#136#179#255'4s'#171#234'D'#148'M'#255'B'#145'K'#255'?'#141'H' - +#255'='#137'E'#255']'#164'e'#255'Z'#160'a'#255'E'#131'K'#255#158#158#158#255 - +#158#158#158#255'`'#146#201#255#158#199#226#255#131#184#218#255'}'#180#215 - +#255'~'#179#215#255'O'#137#180#255';y'#177#234#255#255#255#0#255#255#255#0'w' - +'ww'#255#154#154#154#255'='#138'E'#255'I'#138'O'#255#156#156#156#255#157#157 - +#157#255#157#157#157#255'f'#150#204#255#162#203#227#255#137#189#220#255#131 - +#185#218#255#132#185#218#255'Q'#139#181#255'C~'#182#234#255#255#255#0#255#255 - +#255#0'zzz'#255#153#153#153#255'R'#145'Y'#255#153#154#153#255#155#155#155#255 - +#156#156#156#255#156#156#156#255'l'#154#208#255#167#206#229#255#143#193#223 - +#255#137#189#220#255#139#189#220#255'S'#141#182#255'K'#132#188#234#255#255 - +#255#0#255#255#255#0'}}}'#255#153#153#153#255#153#153#153#255#154#154#154#255 - +#154#154#154#255#155#155#155#255#155#155#155#255'o'#157#211#255#170#209#231 - +#255#171#209#231#255#152#199#225#255#145#194#222#255'V'#143#183#255'R'#137 - +#193#234#255#255#255#0#255#255#255#0#128#128#128#255'~~~'#255'|||'#255'zzz' - +#255'www'#255'uuu'#255'rrr'#255'q'#158#212#255'o'#158#214#255#135#178#220#255 - +#171#211#232#255#169#208#230#255'X'#144#184#255'Y'#142#198#234#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'p'#158#214 - +#219'm'#156#212#255#133#177#218#255'Z'#145#185#255'`'#147#203#234#255#255#255 + +#255#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255#200#154 + +'|'#255#199#152'y'#255#167'`1'#237#186'k7'#254#237#202#179#255#224#162'z'#255 + +#254#250#247#255'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255 + +'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255#253#249#246#255 + +#202#141'e'#255#201#155'|'#255#167'`1'#254#187'l8'#255#238#204#182#255#225 + +#162'z'#255#254#250#247#255#191#220#194#255#191#220#194#255#191#220#194#255 + +#191#220#194#255#191#220#194#255#191#220#194#255#191#220#194#255#191#220#194 + +#255#253#249#246#255#205#144'h'#255#204#158#129#255#168'a2'#255#187'k8'#255 + +#239#206#184#255#225#162'y'#255#254#250#247#255'b'#192#136#255'b'#192#136#255 + +'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255'b'#192#136#255 + +'b'#192#136#255#253#249#246#255#207#147'j'#255#206#163#132#255#170'a2'#255 + +#186'j6'#255#239#208#187#255#226#162'z'#255#254#251#248#255#254#251#248#255 + +#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248#255#254#251#248 + +#255#254#251#248#255#254#251#248#255#254#251#248#255#211#150'm'#255#210#167 + +#138#255#171'b2'#255#187'j6'#255#240#210#190#255#226#163'z'#255#226#163'z' + +#255#225#163'z'#255#226#163'{'#255#225#163'{'#255#224#161'x'#255#222#159'w' + +#255#221#159'v'#255#220#157't'#255#217#155'r'#255#216#153'q'#255#214#153'p' + +#255#213#171#142#255#173'c3'#255#187'j6'#255#242#213#194#255#227#163'z'#255 + +#227#163'z'#255#226#163'{'#255#226#163'{'#255#226#164'{'#255#225#162'y'#255 + +#224#161'x'#255#222#160'w'#255#222#158'u'#255#220#157't'#255#218#155's'#255 + +#217#155's'#255#218#176#149#255#175'd3'#255#187'j6'#255#242#216#197#255#227 + +#164'{'#255#227#163'z'#255#227#164'z'#255#226#164'{'#255#226#163'{'#255#225 + +#163'{'#255#225#162'y'#255#223#160'w'#255#222#159'v'#255#221#158't'#255#219 + +#156'r'#255#220#157't'#255#221#181#154#255#177'e4'#255#187'k6'#255#244#217 + +#199#255#230#166'}'#255#200#140'd'#255#201#141'e'#255#201#142'g'#255#203#146 + +'l'#255#203#146'm'#255#202#144'i'#255#200#140'e'#255#200#140'd'#255#200#140 + +'d'#255#200#140'd'#255#218#156't'#255#225#186#159#255#179'f4'#255#187'k6'#254 + +#244#220#201#255#231#167'}'#255#249#236#225#255#249#236#225#255#249#237#227 + +#255#252#244#238#255#253#250#247#255#253#247#243#255#250#237#229#255#247#231 + +#219#255#247#229#217#255#246#229#216#255#222#160'w'#255#228#190#164#255#180 + +'g4'#255#188'k6'#250#245#221#204#255#231#168'~'#255#250#240#232#255#250#240 + +#232#255#201#141'f'#255#250#240#233#255#253#248#243#255#254#250#248#255#252 + +#244#239#255#249#233#223#255#247#231#219#255#247#229#217#255#224#162'x'#255 + +#231#194#169#255#182'h5'#255#188'k6'#240#246#223#208#255#232#168'~'#255#252 + +#246#241#255#252#246#241#255#200#140'd'#255#250#241#233#255#251#244#238#255 + +#253#250#247#255#253#249#246#255#250#240#232#255#248#232#221#255#247#230#219 + +#255#225#163'z'#255#239#213#195#255#183'i5'#254#188'k6'#216#246#223#209#255 + +#233#170#128#255#254#250#246#255#253#250#246#255#200#140'd'#255#251#243#238 + +#255#251#241#234#255#252#246#242#255#254#251#248#255#252#246#241#255#249#236 + +#226#255#248#231#219#255#238#208#186#255#236#208#189#255#187'p>'#248#188'k6' + +#155#246#224#209#255#247#224#209#255#254#251#248#255#254#251#247#255#253#249 + +#246#255#252#245#240#255#250#240#234#255#251#242#237#255#253#249#246#255#253 + +#250#247#255#251#241#235#255#248#233#223#254#236#208#189#251#201#137'^'#236 + +#181'i5c'#188'k6q'#188'k6'#144#188'k6'#204#188'k6'#238#188'k6'#250#187'k6' + ,#254#187'k6'#255#187'j6'#255#187'j6'#255#188'l9'#255#189'n;'#255#187'm:'#255 + +#187'k8'#239#187'p>'#203#182'i5T'#255#255#255#0#7'OnClick'#7#15'acSaveAsExec' + +'ute'#0#0#9'TMenuItem'#9'MenuItem1'#7'Caption'#6#1'-'#0#0#9'TMenuItem'#7'mnu' + +'Quit'#6'Action'#7#6'AcQuit'#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0 + +#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0 + +#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#29'c'#155#22#25'`'#152'9'#20']'#149'b'#16'Z'#146#136#13'X'#144#164#19'\'#146 + +#252#12'W'#143#237#153#153#153#255'qqq'#255'TTT'#255'QQQ'#255'OOO'#255'LLL' + +#255'JJJ'#255'GGG'#255'EEE'#255'%g'#157#255'2t'#168#255'=|'#175#255'G'#132 + +#181#255'N'#138#186#255'>~'#173#255#12'W'#143#234#255#255#255#0#255#255#255#0 + +'XXX'#255#162#162#162#255#162#162#162#255#163#163#163#255#164#164#164#255#164 + +#164#164#255#165#165#165#255'/o'#165#255'x'#171#210#255'x'#171#211#255's'#167 + +#209#255'i'#160#205#255'@'#127#174#255#15'Y'#145#234#255#255#255#0#255#255 + +#255#0'\\\'#255#161#161#161#255''#255'4~;'#255'1y7' + +#255'.u4'#255'I'#145'P'#255'F'#143'L'#255'9s='#255#161#161#161#255#162#162 + +#162#255'E~'#180#255#136#183#217#255'g'#163#207#255'a'#158#204#255'c'#159#204 + +#255'E'#131#177#255#31'd'#156#234';'#135'B'#255#137#203#146#255#132#200#141 + +#255#128#198#136#255'{'#195#131#255'w'#193#127#255'G'#143'M'#255';t?'#255#161 + +#161#161#255'L'#132#186#255#141#187#219#255'n'#168#209#255'f'#166#209#255'_' + +#180#223#255'G'#133#177#255'%i'#161#234'>'#139'F'#255#143#206#153#255'}'#198 + +#135#255'x'#195#129#255's'#192'|'#255't'#192'|'#255'y'#194#129#255'I'#144'O' + +#255'T'#127'W'#255'T'#137#191#255#148#191#221#255'u'#173#212#255'c'#184#225 + +#255'K'#212#255#255'B'#139#184#255',n'#166#234'A'#144'J'#255#148#210#159#255 + +#145#208#154#255#141#205#150#255#137#203#146#255#132#200#141#255'Q'#152'X' + +#255'A|F'#255#159#159#159#255'Z'#142#196#255#152#195#224#255'|'#179#215#255 + +'t'#175#214#255'^'#196#237#255'K'#136#179#255'4s'#171#234'D'#148'M'#255'B' + +#145'K'#255'?'#141'H'#255'='#137'E'#255']'#164'e'#255'Z'#160'a'#255'E'#131'K' + +#255#158#158#158#255#158#158#158#255'`'#146#201#255#158#199#226#255#131#184 + +#218#255'}'#180#215#255'~'#179#215#255'O'#137#180#255';y'#177#234#255#255#255 + +#0#255#255#255#0'www'#255#154#154#154#255'='#138'E'#255'I'#138'O'#255#156#156 + +#156#255#157#157#157#255#157#157#157#255'f'#150#204#255#162#203#227#255#137 + +#189#220#255#131#185#218#255#132#185#218#255'Q'#139#181#255'C~'#182#234#255 + +#255#255#0#255#255#255#0'zzz'#255#153#153#153#255'R'#145'Y'#255#153#154#153 + +#255#155#155#155#255#156#156#156#255#156#156#156#255'l'#154#208#255#167#206 + +#229#255#143#193#223#255#137#189#220#255#139#189#220#255'S'#141#182#255'K' + +#132#188#234#255#255#255#0#255#255#255#0'}}}'#255#153#153#153#255#153#153#153 + +#255#154#154#154#255#154#154#154#255#155#155#155#255#155#155#155#255'o'#157 + +#211#255#170#209#231#255#171#209#231#255#152#199#225#255#145#194#222#255'V' + +#143#183#255'R'#137#193#234#255#255#255#0#255#255#255#0#128#128#128#255'~~~' + +#255'|||'#255'zzz'#255'www'#255'uuu'#255'rrr'#255'q'#158#212#255'o'#158#214 + +#255#135#178#220#255#171#211#232#255#169#208#230#255'X'#144#184#255'Y'#142 + +#198#234#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0'm'#156#212#137'j'#154#210#251'f'#151#207#238#7'OnClick' - +#7#13'acQuitExecute'#0#0#0#9'TMenuItem'#7'mnuEdit'#7'Caption'#6#4'Edit'#0#9 - +'TMenuItem'#9'MenuItem2'#6'Action'#7#6'AcEdit'#9'AutoCheck'#9#11'Bitmap.Data' - +#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0 - +#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#255#0'p'#158#214#219'm'#156#212#255#133#177#218#255'Z'#145#185#255'`' + +#147#203#234#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#19'@X' - +#255#21'B^'#255'%i'#156#255',v'#180#255';'#139#186#173#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#18'BY'#255']'#156 - +#212#255#166#207#245#255#169#207#236#255'H'#139#193#255',v'#180#255#255#255 - ,#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#30'm'#147#255#203 - +#227#249#255'a'#170#236#255'@'#152#232#255#21'g'#194#255#22'`'#170#255',v' - +#180#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 - +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#30'm'#147#255#200 - +#225#242#255#209#231#250#255'4}'#181#255'1'#153#195#255'm'#196#220#255'J'#156 - +#207#255'4'#131#199#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 - +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0' c'#152' &'#137 - +#185#255#176#203#225#255'g'#169#200#255'`'#220#245#255'D'#214#244#255#142#238 - +#250#255']'#180#230#255';'#143#217#255#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0'&'#137#185#255#190#230#242#255#179#244#252#255'`'#220#245#255 - +'D'#214#244#255#142#238#250#255']'#180#230#255';'#143#217#255#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0'm'#156#212#137'j'#154#210#251'f' + +#151#207#238#7'OnClick'#7#13'acQuitExecute'#0#0#0#9'TMenuItem'#7'mnuEdit'#7 + +'Caption'#6#4'Edit'#0#9'TMenuItem'#9'MenuItem2'#6'Action'#7#6'AcEdit'#9'Auto' + +'Check'#9#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'(' + +#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0 + +#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0''''#144#191#255#195#237#248#255#179 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#19'@X'#255#21'B^'#255'%i'#156#255',v'#180#255';'#139#186 + +#173#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#18'BY'#255']'#156#212#255#166#207#245#255#169#207#236#255'H'#139 + ,#193#255',v'#180#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#30'm'#147#255#203#227#249#255'a'#170#236#255'@'#152#232#255#21'g' + +#194#255#22'`'#170#255',v'#180#255#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#30'm'#147#255#200#225#242#255#209#231#250#255'4}'#181#255'1'#153 + +#195#255'm'#196#220#255'J'#156#207#255'4'#131#199#255#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0' c'#152' &'#137#185#255#176#203#225#255'g'#169#200#255'`'#220 + +#245#255'D'#214#244#255#142#238#250#255']'#180#230#255';'#143#217#255#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0'&'#137#185#255#190#230#242#255#179 +#244#252#255'`'#220#245#255'D'#214#244#255#142#238#250#255']'#180#230#255';' +#143#217#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#255 - +#195#237#248#255#179#244#252#255'`'#220#245#255'D'#214#244#255#142#238#250 - +#255']'#180#230#255';'#143#217#255#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0'/'#186#228#255#195#237#248#255#179#244#252#255'`'#220#245#255'D' - +#214#244#255#142#238#250#255']'#180#230#255';'#143#217#255#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0'/'#186#228#255#195#237#248#255#179#244#252 - +#255'h'#217#245#255'o'#207#243#255'Y'#157#208#255's'#171#221#255'O'#145#201 - +#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#255#195 - +#237#248#255#168#226#248#255'l'#174#221#255#165#207#244#255#165#207#244#255 - +#189#219#247#255'S'#147#203#247#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0'/'#186#228#255#167#212#244#255#197#225#248#255#204#227#249#255#204#227 - +#249#255#189#219#247#255'O'#144#201#253#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0''''#144#191 + +#255#195#237#248#255#179#244#252#255'`'#220#245#255'D'#214#244#255#142#238 + +#250#255']'#180#230#255';'#143#217#255#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0'P'#168#217#255'j'#165#216#255#201#225#247#255 - +#203#227#248#255'B'#149#202#255'1'#130#194#174#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#9'O'#170#219#234'P'#147#202 - +#253'N'#144#200#255'/'#157#210#223'5'#164#222#25#255#255#255#0#255#255#255#0 + +#255#255#255#0'/'#186#228#255#195#237#248#255#179#244#252#255'`'#220#245#255 + +'D'#214#244#255#142#238#250#255']'#180#230#255';'#143#217#255#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#255#195#237#248#255#179#244 + +#252#255'`'#220#245#255'D'#214#244#255#142#238#250#255']'#180#230#255';'#143 + +#217#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#255#195 + +#237#248#255#179#244#252#255'h'#217#245#255'o'#207#243#255'Y'#157#208#255's' + +#171#221#255'O'#145#201#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +'/'#186#228#255#195#237#248#255#168#226#248#255'l'#174#221#255#165#207#244 + +#255#165#207#244#255#189#219#247#255'S'#147#203#247#255#255#255#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0'/'#186#228#255#167#212#244#255#197#225#248#255#204 + +#227#249#255#204#227#249#255#189#219#247#255'O'#144#201#253#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#0#9'TMenuItem'#9'mnuFor' - +'mat'#7'Caption'#6#6'Format'#0#9'TMenuItem'#7'MnuFOnt'#6'Action'#7#6'AcFont' - +#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16 - +#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0'P'#168#217#255'j'#165#216 + +#255#201#225#247#255#203#227#248#255'B'#149#202#255'1'#130#194#174#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'/'#186#228#9'O'#170 + +#219#234'P'#147#202#253'N'#144#200#255'/'#157#210#223'5'#164#222#25#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#0#9'T' + +'MenuItem'#9'mnuFormat'#7'Caption'#6#6'Format'#0#9'TMenuItem'#7'MnuFOnt'#6'A' + +'ction'#7#6'AcFont'#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0 + +'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0 + +#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 @@ -310,110 +311,156 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#206'h8'#174#218#143']'#254#219#144'`'#255 + +#215#131'P'#237#196'P+'#141#189'B#'#16#188'?"*'#195'M+'#162#208'qB'#234#214 + +'~Q'#255#213'|M'#255#198'U1'#189#182'9'#29'1'#255#255#255#0#255#255#255#0#255 + +#255#255#0#202'\/'#10#203'_0D'#225#164'u'#255#218#140'X'#236#198'R,='#255#255 + +#255#0#255#255#255#0#191'E$'#19#195'H'''#127#220#150'k'#255#219#143'`'#255 + +#189'?!T'#182'8'#30#2#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#203']0'#1#211't?'#155#226#169'|'#255#204'f7'#153#255#255#255#0#255#255#255#0 + +#255#255#255#0#204'c7'#177#223#160'w'#255#218#137'Z'#255#187'>""'#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#204 + +'c2'#21#219#147'^'#234#226#167'y'#255#216#131'J'#211#207'j:'#178#206'h9'#181 + ,#213'zE'#216#227#168#129#255#217#134'V'#249#190'A#'#27#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#206'h8'#174#218#143']'#254#219#144'`'#255#215#131'P'#237#196 - +'P+'#141#189'B#'#16#188'?"*'#195'M+'#162#208'qB'#234#214'~Q'#255#213'|M'#255 - +#198'U1'#189#182'9'#29'1'#255#255#255#0#255#255#255#0#255#255#255#0#202'\/' - +#10#203'_0D'#225#164'u'#255#218#140'X'#236#198'R,='#255#255#255#0#255#255#255 - +#0#191'E$'#19#195'H'''#127#220#150'k'#255#219#143'`'#255#189'?!T'#182'8'#30#2 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#203']0'#1#211't?' - +#155#226#169'|'#255#204'f7'#153#255#255#255#0#255#255#255#0#255#255#255#0#204 - +'c7'#177#223#160'w'#255#218#137'Z'#255#187'>""'#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#204'c2'#21#219#147 - +'^'#234#226#167'y'#255#216#131'J'#211#207'j:'#178#206'h9'#181#213'zE'#216#227 - ,#168#129#255#217#134'V'#249#190'A#'#27#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#207'j6\'#223 - +#157'h'#246#218#139'R'#209#203'_1N'#199'Y.*'#205'g7'#160#225#166'z'#255#216 - +#134'S'#242#191'F%'#13#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 - +#0#255#255#255#0#255#255#255#0#255#255#255#0#208'j5'#3#215#128'B'#170#223#159 - +'j'#247#205'f4f'#201']/'#10#204'a3'#136#225#166'y'#255#213'zD'#210#194'J'''#6 + +#207'j6\'#223#157'h'#246#218#139'R'#209#203'_1N'#199'Y.*'#205'g7'#160#225#166 + +'z'#255#216#134'S'#242#191'F%'#13#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#208'j5'#3#215#128'B' + +#170#223#159'j'#247#205'f4f'#201']/'#10#204'a3'#136#225#166'y'#255#213'zD' + +#210#194'J'''#6#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#209'q8"'#222#155'c'#226 + +#220#150'^'#223#207'i5T'#207'i5'#128#226#169'|'#255#209'l:'#165#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#209'q8"'#222#155'c'#226#220#150'^'#223 - +#207'i5T'#207'i5'#128#226#169'|'#255#209'l:'#165#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#212'y'#152#203'_0'#8#255#255#255#0#255#255#255#0#255 + +#255#255#255#0#255#255#255#0#255#255#255#0#214'};'#28#221#150'V'#186#227#169 + +'s'#242#225#168's'#245#225#164'q'#247#211'w>'#152#203'_0'#8#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#215#131'>S'#226#167'l'#222#233#188#145#255#231#186#143 - +#255#231#183#139#255#226#164'q'#251#214'~B'#182#203'\/'#10#255#255#255#0#255 + +#255#0#255#255#255#0#255#255#255#0#215#131'>S'#226#167'l'#222#233#188#145#255 + +#231#186#143#255#231#183#139#255#226#164'q'#251#214'~B'#182#203'\/'#10#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#216#135'@@'#216#139'C'#133#218#142'I'#148#217#137'C'#143 - +#216#132'@'#144#216#132'A'#154#211's:'#138#204'b2'#13#201'[.'#1#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#0#255#255#255#0#255#255#255#0#216#135'@@'#216#139'C'#133#218#142'I'#148 + +#217#137'C'#143#216#132'@'#144#216#132'A'#154#211's:'#138#204'b2'#13#201'[.' + +#1#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#0#0#9'TMenuItem'#15'MnuTextRotation'#7'Caption'#6#13'Text rotation'#0#9'TMe' - +'nuItem'#10'MenuItem29'#6'Action'#7#11'AcTextHoriz'#9'AutoCheck'#9#0#0#9'TMe' - +'nuItem'#10'MenuItem30'#6'Action'#7#12'AcTextVertCW'#9'AutoCheck'#9#0#0#9'TM' - +'enuItem'#10'MenuItem31'#6'Action'#7#13'AcTextVertCCW'#9'AutoCheck'#9#0#0#9 - +'TMenuItem'#10'MenuItem32'#6'Action'#7#13'AcTextStacked'#9'AutoCheck'#9#0#0#0 - +#9'TMenuItem'#15'MnuHorAlignment'#7'Caption'#6#20'Horizontal alignment'#0#9 - +'TMenuItem'#13'MnuHorDefault'#6'Action'#7#17'AcHorDefaultAlign'#9'AutoCheck' - +#9#0#0#9'TMenuItem'#9'MenuItem6'#7'Caption'#6#1'-'#0#0#9'TMenuItem'#9'MenuIt' - +'em3'#6'Action'#7#11'AcLeftAlign'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0#0 - +'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0 - +#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#255#0#0#0#9'TMenuItem'#15'MnuTextRotation'#7'Caption'#6#13'Text rot' + +'ation'#0#9'TMenuItem'#10'MenuItem29'#6'Action'#7#11'AcTextHoriz'#9'AutoChec' + +'k'#9#0#0#9'TMenuItem'#10'MenuItem30'#6'Action'#7#12'AcTextVertCW'#9'AutoChe' + +'ck'#9#0#0#9'TMenuItem'#10'MenuItem31'#6'Action'#7#13'AcTextVertCCW'#9'AutoC' + +'heck'#9#0#0#9'TMenuItem'#10'MenuItem32'#6'Action'#7#13'AcTextStacked'#9'Aut' + +'oCheck'#9#0#0#0#9'TMenuItem'#15'MnuHorAlignment'#7'Caption'#6#20'Horizontal' + +' alignment'#0#9'TMenuItem'#13'MnuHorDefault'#6'Action'#7#17'AcHorDefaultAli' + +'gn'#9'AutoCheck'#9#0#0#9'TMenuItem'#9'MenuItem6'#7'Caption'#6#1'-'#0#0#9'TM' + +'enuItem'#9'MenuItem3'#6'Action'#7#11'AcLeftAlign'#9'AutoCheck'#9#11'Bitmap.' + +'Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16 + +#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +'<<<'#255'555'#255'///'#255')))'#255'$$$'#255#30#30#30#255#25#25#25#255#20#20 - +#20#255#15#15#15#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#255#0'<<<'#255'555'#255'///'#255')))'#255'$$$'#255#30#30#30#255#25 + +#25#25#255#20#20#20#255#15#15#15#255#255#255#255#0#255#255#255#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0'JJJ'#255'DDD'#255'>>>'#255'888' - +#255'222'#255',,,'#255'&&&'#255' '#255#27#27#27#255#22#22#22#255#17#17#17 - +#255#12#12#12#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'XXX' - +#255'RRR'#255'LLL'#255'FFF'#255'@@@'#255':::'#255'444'#255'...'#255'((('#255 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'JJJ'#255'DDD'#255'>' + +'>>'#255'888'#255'222'#255',,,'#255'&&&'#255' '#255#27#27#27#255#22#22#22 + +#255#17#17#17#255#12#12#12#255#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'OOO'#255'III' - +#255'CCC'#255'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255#255#255#0#255 + +#255#255#0'XXX'#255'RRR'#255'LLL'#255'FFF'#255'@@@'#255':::'#255'444'#255'..' + +'.'#255'((('#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'O' + +'OO'#255'III'#255'CCC'#255'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 ,#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0'ooo'#255'kkk'#255'fff'#255'aaa'#255'\' - +'\\'#255'WWW'#255'QQQ'#255'KKK'#255'EEE'#255#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'ooo'#255'kkk'#255'fff' + +#255'aaa'#255'\\\'#255'WWW'#255'QQQ'#255'KKK'#255'EEE'#255#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'vvv'#255 - +'ttt'#255'ppp'#255'lll'#255'hhh'#255'ccc'#255'^^^'#255'YYY'#255'SSS'#255'NNN' - +#255'HHH'#255'BBB'#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0'vvv'#255'ttt'#255'ppp'#255'lll'#255'hhh'#255'ccc'#255'^^^'#255'YYY' + +#255'SSS'#255'NNN'#255'HHH'#255'BBB'#255#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#9'TMe' + +'nuItem'#9'MenuItem4'#6'Action'#7#16'AcHorCenterAlign'#9'AutoCheck'#9#11'Bit' + +'map.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0 + +#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0'555k///'#255')))'#255'$$$'#255#30#30#30 + +#255#25#25#25#255#20#20#20#255#15#15#15#255#11#11#11#255#7#7#7'k'#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'JJJ'#255'DDD'#255'>' + +'>>'#255'888'#255'222'#255',,,'#255'&&&'#255' '#255#27#27#27#255#22#22#22 + +#255#17#17#17#255#12#12#12#255#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0'RRRkLLL'#255'FFF'#255'@@@'#255':::'#255'444'#255'..' + +'.'#255'((('#255'###'#255#29#29#29'k'#255#255#255#0#255#255#255#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'OOO'#255'II' + +'I'#255'CCC'#255'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'kkkkfff'#255'aaa' + +#255'\\\'#255'WWW'#255'QQQ'#255'KKK'#255'EEE'#255'???'#255'999k'#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'vvv'#255'ttt'#255'p' + +'pp'#255'lll'#255'hhh'#255'ccc'#255'^^^'#255'YYY'#255'SSS'#255'NNN'#255'HHH' + +#255'BBB'#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + ,#255#255#0#255#255#255#0#255#255#255#0#0#0#9'TMenuItem'#9'MenuItem5'#6'Actio' + +'n'#7#12'AcRightAlign'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'B' + +'M6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0 + +#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255 +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#9'TMenuItem'#9'MenuItem' - +'4'#6'Action'#7#16'AcHorCenterAlign'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0 - +#0'6'#4#0#0'BM6'#4#0#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0 - +#0#0#0#0#4#0#0'd'#0#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0'555k///'#255')))'#255'$$$'#255#30#30#30#255#25#25#25#255#20#20 - +#20#255#15#15#15#255#11#11#11#255#7#7#7'k'#255#255#255#0#255#255#255#0#255 + +#255#255#255#0#255#255#255#0')))'#255'$$$'#255#30#30#30#255#25#25#25#255#20 + +#20#20#255#15#15#15#255#11#11#11#255#7#7#7#255#3#3#3#255#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 @@ -423,95 +470,48 @@ LazarusResources.Add('TForm1','FORMDATA',[ +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0'RRRkLLL'#255'FFF'#255'@@@'#255':::'#255'444'#255'...'#255'((('#255'##' - +'#'#255#29#29#29'k'#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#0#255#255#255#0#255#255#255#0'FFF'#255'@@@'#255':::'#255'444'#255'...' + +#255'((('#255'###'#255#29#29#29#255#24#24#24#255#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'OOO'#255'III'#255'CCC'#255 - +'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0'kkkkfff'#255'aaa'#255'\\\'#255'WWW' - +#255'QQQ'#255'KKK'#255'EEE'#255'???'#255'999k'#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'OOO'#255'II' + +'I'#255'CCC'#255'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0'vvv'#255'ttt'#255'ppp'#255'lll' - +#255'hhh'#255'ccc'#255'^^^'#255'YYY'#255'SSS'#255'NNN'#255'HHH'#255'BBB'#255 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0'aaa'#255'\\\'#255'WWW'#255'QQQ'#255'KKK'#255'EEE'#255'???'#255'99' + +'9'#255'333'#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'vvv' + +#255'ttt'#255'ppp'#255'lll'#255'hhh'#255'ccc'#255'^^^'#255'YYY'#255'SSS'#255 + +'NNN'#255'HHH'#255'BBB'#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255 + +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - ,#255#255#255#0#255#255#255#0#0#0#9'TMenuItem'#9'MenuItem5'#6'Action'#7#12'Ac' - +'RightAlign'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0#0 - +#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0#0 - +'d'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0')))'#255'$$$'#255#30#30#30#255#25#25#25#255#20#20#20#255 - +#15#15#15#255#11#11#11#255#7#7#7#255#3#3#3#255#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0'JJJ'#255'DDD'#255'>>>'#255'888'#255'222'#255',,' - +','#255'&&&'#255' '#255#27#27#27#255#22#22#22#255#17#17#17#255#12#12#12#255 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0'FFF'#255'@@@'#255':::'#255'444'#255'...'#255'(((' - +#255'###'#255#29#29#29#255#24#24#24#255#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0'ddd'#255'___'#255'ZZZ'#255'TTT'#255'OOO'#255'III'#255'C' - +'CC'#255'==='#255'777'#255'111'#255'+++'#255'%%%'#255#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +'aaa'#255'\\\'#255'WWW'#255'QQQ'#255'KKK'#255'EEE'#255'???'#255'999'#255'333' - +#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'vvv'#255'ttt' - +#255'ppp'#255'lll'#255'hhh'#255'ccc'#255'^^^'#255'YYY'#255'SSS'#255'NNN'#255 - +'HHH'#255'BBB'#255#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#0#9'TMenuItem'#16'M' + +'nuVertAlignment'#7'Caption'#6#18'Vertical alignment'#0#9'TMenuItem'#14'MnuV' + +'ertDefault'#6'Action'#7#15'AcVAlignDefault'#9'AutoCheck'#9#0#0#9'TMenuItem' + +#9'MenuItem7'#7'Caption'#6#1'-'#0#0#9'TMenuItem'#10'MnuVertTop'#6'Action'#7 + +#11'AcVAlignTop'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0 + +#0#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0 + +#0#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#0#0#0#9'TMenuItem'#16'MnuVe' - +'rtAlignment'#7'Caption'#6#18'Vertical alignment'#0#9'TMenuItem'#14'MnuVertD' - +'efault'#6'Action'#7#15'AcVAlignDefault'#9'AutoCheck'#9#0#0#9'TMenuItem'#9'M' - +'enuItem7'#7'Caption'#6#1'-'#0#0#9'TMenuItem'#10'MnuVertTop'#6'Action'#7#11 - +'AcVAlignTop'#9'AutoCheck'#9#11'Bitmap.Data'#10':'#4#0#0'6'#4#0#0'BM6'#4#0#0 - +#0#0#0#0'6'#0#0#0'('#0#0#0#16#0#0#0#16#0#0#0#1#0' '#0#0#0#0#0#0#4#0#0'd'#0#0 - +#0'd'#0#0#0#0#0#0#0#0#0#0#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 - +#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 - +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0';;;'#0'444'#0'...'#0'((' - +'('#0'###'#0#29#29#29#0#24#24#24#0#19#19#19#0#15#15#15#0#11#11#11#0#7#7#7#0#3 - +#3#3#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0 - +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 + +#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0';;;'#0'444'#0'...'#0 + +'((('#0'###'#0#29#29#29#0#24#24#24#0#19#19#19#0#15#15#15#0#11#11#11#0#7#7#7#0 + +#3#3#3#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255 + +#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255 +#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255 ,#255#0#255#255#255#0#255#255#255#0#255#255#255#0#255#255#255#0'III'#0'CCC'#0 +'==='#0'777'#0'111'#0'+++'#0'%%%'#0#31#31#31#0#26#26#26#0#21#21#21#0#17#17#17 diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 8dd1a66f5..5684e9eb7 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -137,10 +137,11 @@ type TsUsedFormattingFields = set of TsUsedFormattingField; {@@ Number/cell formatting. Only uses a subset of the default formats, - enough to be able to read/write date values. } + enough to be able to read/write date/time values. + nfCustom allows to apply a format string directly. } TsNumberFormat = (nfGeneral, nfFixed, nfFixedTh, nfExp, nfSci, nfPercentage, - nfShortDateTime, nfFmtDateTime, nfShortDate, nfShortTime, nfLongTime, - nfShortTimeAM, nfLongTimeAM, nfTimeInterval); + nfShortDateTime, nfFmtDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime, + nfShortTimeAM, nfLongTimeAM, nfTimeInterval, nfCustom); {@@ Text rotation formatting. The text is rotated relative to the standard orientation, which is from left to right horizontal: @@ -364,14 +365,17 @@ type { Writing of values } procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; - AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); + AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); overload; + procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; + AFormatString: String); overload; procedure WriteBlank(ARow, ACol: Cardinal); procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); { Writing of cell attributes } - procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat); + procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat; + const AFormatString: String); function WriteFont(ARow, ACol: Cardinal; const AFontName: String; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; procedure WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer); overload; @@ -481,6 +485,50 @@ type property FileFormat: TsSpreadsheetFormat read FFormat; end; + + {@@ Contents of the format record } + + TsNumFormatData = class + public + Index: Integer; + NumFormat: TsNumberFormat; + Decimals: Integer; + FormatString: string; + end; + + {@@ Specialized list for number format items } + + TsCustomNumFormatList = class(TFPList) + private + function GetItem(AIndex: Integer): TsNumFormatData; + procedure SetItem(AIndex: Integer; AValue: TsNumFormatData); + protected + FFirstFormatIndexInFile: Integer; + FNextFormatIndex: Integer; + procedure AddBuiltinFormats; virtual; + procedure Analyze(var AFormatString: String; var ANumFormat: TsNumberFormat; + var ADecimals: Word); virtual; + procedure RemoveFormat(AIndex: Integer); + public + constructor Create; + destructor Destroy; override; + function AddFormat(AFormatCell: PCell): Integer; overload; + function AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat; + AFormatString: String = ''; ADecimals: Integer = 0): Integer; overload; + procedure AnalyzeAndAdd(AFormatIndex: Integer; AFormatString: String); + procedure Clear; + procedure Delete(AIndex: Integer); + function Find(AFormatCell: PCell): integer; overload; + function Find(ANumFormat: TsNumberFormat; AFormatString: String; + ADecimals: Integer): Integer; overload; + function Find(AFormatIndex: Integer): Integer; overload; + function FormatStringForWriting(AIndex: Integer): String; virtual; + procedure Sort; + + property FirstFormatIndexInFile: Integer read FFirstFormatIndexInFile; + property Items[AIndex: Integer]: TsNumFormatData read GetItem write SetItem; default; + end; + {@@ TsSpreadReader class reference type } TsSpreadReaderClass = class of TsCustomSpreadReader; @@ -491,6 +539,8 @@ type protected FWorkbook: TsWorkbook; FWorksheet: TsWorksheet; + FNumFormatList: TsCustomNumFormatList; + procedure CreateNumFormatList; virtual; abstract; { Record reading methods } procedure ReadBlank(AStream: TStream); virtual; abstract; procedure ReadFormula(AStream: TStream); virtual; abstract; @@ -498,11 +548,13 @@ type procedure ReadNumber(AStream: TStream); virtual; abstract; public constructor Create(AWorkbook: TsWorkbook); virtual; // To allow descendents to override it + destructor Destroy; override; { General writing methods } procedure ReadFromFile(AFileName: string; AData: TsWorkbook); virtual; procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); virtual; procedure ReadFromStrings(AStrings: TStrings; AData: TsWorkbook); virtual; property Workbook: TsWorkbook read FWorkbook; + property NumFormatList: TsCustomNumFormatList read FNumFormatList; end; {@@ TsSpreadWriter class reference type } @@ -517,12 +569,17 @@ type private FWorkbook: TsWorkbook; protected + FNumFormatList: TsCustomNumFormatList; { Helper routines } procedure AddDefaultFormats(); virtual; + procedure CreateNumFormatList; virtual; abstract; function ExpandFormula(AFormula: TsFormula): TsExpandedFormula; function FindFormattingInList(AFormat: PCell): Integer; + procedure FixFormat(ACell: PCell); virtual; procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); procedure ListAllFormattingStyles; + procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream); + procedure ListAllNumFormats; virtual; { Helpers for writing } procedure WriteCellCallback(ACell: PCell; AStream: TStream); procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree); @@ -541,12 +598,14 @@ type FFormattingStyles: array of TCell; NextXFIndex: Integer; // Indicates which should be the next XF (Style) Index when filling the styles list constructor Create(AWorkbook: TsWorkbook); virtual; // To allow descendents to override it + destructor Destroy; override; { General writing methods } procedure IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback); procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); virtual; procedure WriteToStream(AStream: TStream); virtual; procedure WriteToStrings(AStrings: TStrings); virtual; property Workbook: TsWorkbook read FWorkbook; + property NumFormatList: TsCustomNumFormatList read FNumFormatList; end; {@@ List of registered formats } @@ -615,10 +674,6 @@ procedure RegisterSpreadFormat( AFormat: TsSpreadsheetFormat); function GetFileFormatName(AFormat: TsSpreadsheetFormat): String; - -function SciFloat(AValue: Double; ADecimals: Word): String; -function TimeIntervalToString(AValue: TDateTime): String; - procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer); implementation @@ -731,50 +786,6 @@ begin end; -{@@ - Formats the number AValue in "scientific" format with the given number of - decimals. "Scientific" is the same as "exponential", but with exponents rounded - to multiples of 3. -} -function SciFloat(AValue: Double; ADecimals: Word): String; -var - m: Double; - ex: Integer; -begin - if AValue = 0 then - Result := '0.0' - else begin - ex := floor(log10(abs(AValue))); // exponent - // round exponent to multiples of 3 - ex := (ex div 3) * 3; - if ex < 0 then dec(ex, 3); - m := AValue * Power(10, -ex); // mantisse - Result := Format('%.*fE%d', [ADecimals, m, ex]); - end; -end; - -{@@ - Formats the number AValue as a time string with hours, minutes and seconds. - Unlike TimeToStr there can be more than 24 hours. -} -function TimeIntervalToString(AValue: TDateTime): String; -var - hrs: Integer; - diff: Double; - h,m,s,z: Word; - ts: String; -begin - ts := DefaultFormatSettings.TimeSeparator; - DecodeTime(frac(abs(AValue)), h, m, s, z); - hrs := h + trunc(abs(AValue))*24; - if z > 499 then inc(s); - if hrs > 0 then - Result := Format('%d%s%.2d%s%.2d', [hrs, ts, m, ts, s]) - else - Result := Format('%d%s%.2d', [m, ts, s]); - if AValue < 0.0 then Result := '-' + Result; -end; - {@@ If a palette is coded as big-endian (e.g. by copying the rgb values from the OpenOffice doc) the palette values can be converted by means of this @@ -1109,31 +1120,33 @@ end; function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; function FloatToStrNoNaN(const Value: Double; - ANumberFormat: TsNumberFormat; ANumberFormatStr: ansistring): ansistring; + ANumberFormat: TsNumberFormat; ANumberFormatStr: string; ADecimals: Word): ansistring; begin if IsNan(Value) then Result := '' else if ANumberFormat = nfSci then - Result := SciFloat(Value, 1) + Result := SciFloat(Value, ADecimals) else if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then Result := FloatToStr(Value) else if (ANumberFormat = nfPercentage) then - Result := FormatFloat(ANumberFormatStr, Value*100) + '%' + Result := FormatFloat(ANumberFormatStr, Value*100) // '%' is already added to FormatStr. else Result := FormatFloat(ANumberFormatStr, Value); end; function DateTimeToStrNoNaN(const Value: Double; - ANumberFormat: TsNumberFormat; ANumberFormatStr: String): ansistring; + ANumberFormat: TsNumberFormat; ANumberFormatStr: String; ADecimals: Word): ansistring; begin Result := ''; if not IsNaN(Value) then begin + (* if ANumberFormat = nfTimeInterval then - Result := TimeIntervalToString(Value) + Result := TimeIntervalToString(Value, ANumberFormatStr) else + *) if ANumberFormatStr = '' then Result := FormatDateTime('c', Value) else @@ -1152,14 +1165,17 @@ begin Exit; end; - case ACell^.ContentType of - //cctFormula - cctNumber: Result := FloatToStrNoNaN(ACell^.NumberValue, ACell^.NumberFormat, ACell^.NumberFormatStr); - cctUTF8String: Result := ACell^.UTF8StringValue; - cctDateTime: Result := DateTimeToStrNoNaN(ACell^.DateTimeValue, ACell^.NumberFormat, ACell^.NumberFormatStr); - else - Result := ''; - end; + with ACell^ do + case ContentType of + cctNumber: + Result := FloatToStrNoNaN(NumberValue, NumberFormat, NumberFormatStr, NumberDecimals); + cctUTF8String: + Result := UTF8StringValue; + cctDateTime: + Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr, NumberDecimals); + else + Result := ''; + end; end; function TsWorksheet.ReadAsNumber(ARow, ACol: Cardinal): Double; @@ -1322,14 +1338,33 @@ begin nfExp: ACell^.NumberFormatStr := '0' + decs + 'E+00'; nfSci: - ACell^.NumberFormatStr := ''; + ACell^.NumberFormatStr := '##0' + decs + 'E+0'; nfPercentage: - ACell^.NumberFormatStr := '0' + decs; + ACell^.NumberFormatStr := '0' + decs + '%'; end; end; ChangedCell(ARow, ACol); end; +{@@ + Writes a floating point number to the cell and uses a custom number format + specified by the format string. + NOTE that fpspreadsheet may not be able to detect the formatting when reading + the file. } +procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: Double; + AFormatString: String); +var + ACell: PCell; +begin + ACell := GetCell(ARow, ACol); + Include(ACell^.UsedFormattingFields, uffNumberFormat); + ACell^.ContentType := cctNumber; + ACell^.NumberValue := ANumber; + ACell^.NumberFormat := nfCustom; + ACell^.NumberFormatStr := AFormatString; + ChangedCell(ARow, ACol); +end; + {@@ Writes as empty cell @@ -1385,6 +1420,8 @@ begin ACell^.NumberFormatStr := FormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat; nfShortDate: ACell^.NumberFormatStr := FormatSettings.ShortDateFormat; + nfLongDate: + ACell^.NumberFormatStr := 'dd/mmm/yyyy'; nfShortTime: ACell^.NumberFormatStr := 't'; nfLongTime: @@ -1397,13 +1434,14 @@ begin begin fmt := lowercase(AFormatStr); if fmt = 'dm' then ACell^.NumberFormatStr := 'd/mmm' - else if fmt = 'my' then ACell^.NumberFormatSTr := 'mmm/yy' - else if fmt = 'ms' then ACell^.NumberFormatStr := 'nn:ss' - else if fmt = 'msz' then ACell^.NumberFormatStr := 'nn:ss.z' + else if fmt = 'my' then ACell^.NumberFormatStr := 'mmm/yy' + else if fmt = 'ms' then ACell^.NumberFormatStr := 'mm:ss' // Excel does not like the "n" + else if fmt = 'msz' then ACell^.NumberFormatStr := 'mm:ss.z' else ACell^.NumberFormatStr := AFormatStr; end; nfTimeInterval: - ACell^.NumberFormatStr := ''; + if AFormatStr = '' then ACell^.NumberFormatStr := '[h]:mm:ss' + else ACell^.NumberFormatStr := AFormatStr; end; ChangedCell(ARow, ACol); end; @@ -1431,17 +1469,19 @@ end; @param ARow The row of the cell @param ACol The column of the cell @param TsNumberFormat What format to apply + @param string Formatstring @see TsNumberFormat } procedure TsWorksheet.WriteNumberFormat(ARow, ACol: Cardinal; - ANumberFormat: TsNumberFormat); + ANumberFormat: TsNumberFormat; const AFormatString: String); var ACell: PCell; begin ACell := GetCell(ARow, ACol); Include(ACell^.UsedFormattingFields, uffNumberFormat); ACell^.NumberFormat := ANumberFormat; + ACell^.NumberFormatStr := AFormatString; ChangedCell(ARow, ACol); end; @@ -2408,12 +2448,310 @@ begin {$ENDIF} end; + +{ TsCustomNumFormatList } + +constructor TsCustomNumFormatList.Create; +begin + inherited Create; + AddBuiltinFormats; +end; + +destructor TsCustomNumFormatList.Destroy; +begin + Clear; + inherited Destroy; +end; + +{ Adds a new number format data to the list and returns the list index of the + new (or present) item. } +function TsCustomNumFormatList.AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat; + AFormatString: String = ''; ADecimals: Integer = 0): integer; +var + item: TsNumFormatData; +begin + item := TsNumFormatData.Create; + item.Index := AFormatIndex; + item.NumFormat := ANumFormat; + item.FormatString := AFormatString; + item.Decimals := ADecimals; + Result := inherited Add(item); +end; + +function TsCustomNumFormatList.AddFormat(AFormatCell: PCell): Integer; +var + item: TsNumFormatData; +begin + if AFormatCell = nil then + raise Exception.Create('TsCustomNumFormat.Add: No nil pointers please'); + + if Count = 0 then + raise Exception.Create('TsCustomNumFormatList: Error in program logics: You must provide built-in formats first.'); + + Result := AddFormat(FNextFormatIndex, AFormatCell^.NumberFormat, + AFormatCell^.NumberFormatStr, AFormatCell^.NumberDecimals); + + inc(FNextFormatIndex); +end; + +{ Adds the builtin format items to the list. Must be called before user items + are added. Must specify FFirstFormatIndexInFile (BIFF5-8, e.g. don't save + formats <164) and must initialize the index of the first user format + (FNextFormatIndex) which is automatically incremented when adding user formats. } +procedure TsCustomNumFormatList.AddBuiltinFormats; +begin + // must be overridden +end; + +{ Takes the format string (AFormatString) as it is read from the file and + extracts the number format type (ANumFormat) and the number of decimals + (ADecimals) out of it. If the format string cannot be directly handled by + fpc it has to be transformed to make it compatible. Can be done in + overridden versions which know more about the structure of the string in + the actual file format. } +procedure TsCustomNumFormatList.Analyze(var AFormatString: String; + var ANumFormat: TsNumberFormat; var ADecimals: Word); +const + SHORT_LONG_DATE: array[boolean] of TsNumberFormat = ( + nfShortDate, nfLongDate + ); + AMPM_SHORT_LONG_TIME: array[boolean, boolean] of TsNumberFormat = ( + (nfShortTime, nfLongTime), + (nfShortTimeAM, nfLongTimeAM) + ); + EXP_SCI: array[boolean] of TsNumberFormat = ( + nfExp, nfSci + ); +var + decs: Word; + isAMPM: Boolean; + isLongTime: Boolean; + isLongDate: Boolean; + isInterval: Boolean; + isSci: Boolean; + isTime, isDate: Boolean; +begin + ANumFormat := nfGeneral; + if IsPercentNumberFormat(AFormatString, ADecimals) then + ANumFormat := nfPercentage + else + if IsExpNumberFormat(AFormatstring, ADecimals, isSci) then + ANumFormat := EXP_SCI[isSci] + else + if IsThousandSepNumberFormat(AFormatString, ADecimals) then + ANumFormat := nfFixedTh + else + if IsFixedNumberFormat(AFormatString, ADecimals) then + ANumFormat := nfFixed + else begin + isTime := IsTimeFormat(AFormatString, isLongTime, isAMPM, isInterval, ADecimals); + isDate := IsDateFormat(AFormatString, isLongDate); + if isInterval then + ANumFormat := nfTimeInterval + else + if isDate and isTime then + ANumFormat := nfShortDateTime + else if isDate then + ANumFormat := SHORT_LONG_DATE[isLongDate] + else if isTime then + ANumFormat := AMPM_SHORT_LONG_TIME[isAMPM, isLongTime] + else if AFormatString <> '' then + ANumFormat := nfCustom; + end; +end; + +{ Called from the reader when a format item has been read from the file. + Determines the numFormat type, format string etc and stores the format in the + list. If necessary, the format string has to be made compatible with fpc + afterwards - it is used directly for getting the cell text. } +procedure TsCustomNumFormatList.AnalyzeAndAdd(AFormatIndex: Integer; + AFormatString: String); +var + nf: TsNumberFormat; + decs: Word; +begin + if Find(AFormatIndex) > -1 then + raise Exception.Create('TsCustomNumFormatList.AnalyzeAndAdd: Format index must be unique.'); + + // Analyze the format string and extract information for internal formatting + Analyze(AFormatString, nf, decs); + + // Add the new item + AddFormat(AFormatIndex, nf, AFormatString, decs); +end; + +{ Clears the list and frees memory occupied by the format items. } +procedure TsCustomNumFormatList.Clear; +var + i: Integer; +begin + for i:=0 to Count-1 do RemoveFormat(i); + inherited Clear; +end; + +{ Deletes a format item from the list, and makes sure that its memory is + released. } +procedure TsCustomNumFormatList.Delete(AIndex: Integer); +begin + RemoveFormat(AIndex); + Delete(AIndex); +end; + +{ Determines whether the format attributed to the given cell is already + contained in the list and returns its list index. } +function TsCustomNumFormatList.Find(AFormatCell: PCell): integer; +begin + if AFormatCell = nil then + Result := -1 + else + Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr, AFormatCell^.NumberDecimals); +end; + +{ Seeks a format item with the given properties and returns its list index, + or -1 if not found. } +function TsCustomNumFormatList.Find(ANumFormat: TsNumberFormat; + AFormatString: String; ADecimals: Integer): Integer; +var + item: TsNumFormatData; + fmt: String; + itemfmt: String; +begin + // These are pre-defined formats - no need to check format string & decimals + if ANumFormat in [ nfGeneral, nfShortDateTime, nfShortDate, nfLongDate, + nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM ] + then + for Result := 0 to Count-1 do begin + item := Items[Result]; + if (item <> nil) and (item.NumFormat = ANumFormat) then + exit; + end + else + if (ANumFormat = nfFmtDateTime) then begin + fmt := lowercase(AFormatString); + for Result := 0 to Count-1 do begin + item := Items[Result]; + if (item <> nil) and (item.NumFormat = nfFmtDateTime) then begin + itemfmt := lowercase(item.FormatString); + if ((itemfmt = 'dm') or (itemfmt = 'd-mmm') or (itemfmt = 'd mmm') or (itemfmt = 'd. mmm') or (itemfmt ='d/mmm')) + and ((fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm')) + then + exit; + if ((itemfmt = 'my') or (itemfmt = 'mmm-yy') or (itemfmt = 'mmm yyy') or (itemfmt = 'mmm/yy')) + and ((fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy')) + then + exit; + if ((itemfmt = 'ms') or (itemfmt = 'nn:ss') or (itemfmt = 'mm:ss')) + and ((fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss')) + then + exit; + if ((itemfmt = 'msz') or (itemfmt = 'mm:ss.z') or (itemfmt = 'mm:ss.0')) + and ((fmt = 'msz') or (fmt = 'mm:ss.z') or (fmt = 'mm:ss.0')) + then + exit; + end; + end; + for Result := 0 to Count-1 do begin + item := Items[Result]; + if fmt = lowercase(item.FormatString) then + exit; + end; + end else + // Check only the format string for nfCustom. + if (ANumFormat = nfCustom) then begin + for Result := 0 to Count-1 do begin + item := Items[Result]; + if (item <> nil) + and (item.NumFormat = ANumFormat) + and (item.FormatString = AFormatString) + then + exit; + end; + end else + // The other formats can carry additional information + for Result := 0 to Count-1 do begin + item := Items[Result]; + if (item <> nil) + and (item.NumFormat = ANumFormat) + and (item.FormatString = AFormatString) + and (item.Decimals = ADecimals) + then + exit; + end; + Result := -1; +end; + +{ Finds the item with the given format index and returns its index in + the format list. } +function TsCustomNumFormatList.Find(AFormatIndex: Integer): integer; +var + item: TsNumFormatData; +begin + for Result := 0 to Count-1 do begin + item := Items[Result]; + if item.Index = AFormatIndex then + exit; + end; + Result := -1; +end; + +{ Determines the format string to be written into the spreadsheet file. + Needs to be overridden if the format strings are different from the fpc + convention. } +function TsCustomNumFormatList.FormatStringForWriting(AIndex: Integer): String; +var + item: TsNumFormatdata; +begin + item := Items[AIndex]; + if item <> nil then Result := item.FormatString else Result := ''; +end; + +function TsCustomNumFormatList.GetItem(AIndex: Integer): TsNumFormatData; +begin + Result := TsNumFormatData(inherited Items[AIndex]); +end; + +{ Deletes the memory occupied by the formatting data, but keeps the item in then + list to maintain the indexes of followint items. } +procedure TsCustomNumFormatList.RemoveFormat(AIndex: Integer); +var + item: TsNumFormatData; +begin + item := GetItem(AIndex); + if item <> nil then begin + item.Free; + SetItem(AIndex, nil); + end; +end; + +procedure TsCustomNumFormatList.SetItem(AIndex: Integer; AValue: TsNumFormatData); +begin + inherited Items[AIndex] := AValue; +end; + +function CompareNumFormatData(Item1, Item2: Pointer): Integer; +begin + Result := CompareValue(TsNumFormatData(Item1).Index, TsNumFormatData(Item2).Index); +end; + +{ Sorts the format data items in ascending order of the format indexes. } +procedure TsCustomNumFormatList.Sort; +begin + inherited Sort(@CompareNumFormatData); +end; + { TsCustomSpreadReader } constructor TsCustomSpreadReader.Create(AWorkbook: TsWorkbook); begin inherited Create; FWorkbook := AWorkbook; + CreateNumFormatList; +end; + +destructor TsCustomSpreadReader.Destroy; +begin + FNumFormatList.Free; + inherited Destroy; end; {@@ @@ -2471,6 +2809,13 @@ constructor TsCustomSpreadWriter.Create(AWorkbook: TsWorkbook); begin inherited Create; FWorkbook := AWorkbook; + CreateNumFormatList; +end; + +destructor TsCustomSpreadWriter.Destroy; +begin + FNumFormatList.Free; + inherited Destroy; end; {@@ @@ -2522,10 +2867,10 @@ begin if uffNumberFormat in AFormat^.UsedFormattingFields then begin if (FFormattingStyles[i].NumberFormat <> AFormat^.NumberFormat) then Continue; case AFormat^.NumberFormat of - nfFixed, nfFixedTh, nfPercentage, nfExp: + nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci: if (FFormattingStyles[i].NumberDecimals <> AFormat^.NumberDecimals) then Continue; - nfShortDate, nfShortDateTime, nfShortTime, nfLongTime, nfShortTimeAM, - nfLongTimeAM, nfFmtDateTime, nfTimeInterval: + nfShortDate, nfLongDate, nfShortDateTime, nfShortTime, nfLongTime, + nfShortTimeAM, nfLongTimeAM, nfFmtDateTime, nfTimeInterval, nfCustom: if (FFormattingstyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue; end; end; @@ -2538,6 +2883,20 @@ begin end; end; +{ If formatting features of a cell are not supported by the destination file + format of the writer, here is the place to apply replacements. + Must be overridden by descendants. See BIFF2 } +procedure TsCustomSpreadWriter.FixFormat(ACell: PCell); +var + isLong, isAMPM, isInterval: Boolean; + decs: Word; +begin + if ACell^.NumberFormat = nfFmtDateTime then begin + if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then + ACell^.NumberDecimals := decs; + end; +end; + { Each descendent should define it's own default formats, if any. Always add the normal, unformatted style first to speed up. } procedure TsCustomSpreadWriter.AddDefaultFormats(); @@ -2550,8 +2909,9 @@ procedure TsCustomSpreadWriter.ListAllFormattingStylesCallback(ACell: PCell; ASt var Len: Integer; begin - if ACell^.UsedFormattingFields = [] then Exit; + FixFormat(ACell); + if ACell^.UsedFormattingFields = [] then Exit; if FindFormattingInList(ACell) <> -1 then Exit; Len := Length(FFormattingStyles); @@ -2575,6 +2935,30 @@ begin end; end; +{@@ + Adds the number format of the given cell to the NumFormatList, but only if + it does not yet exist in the list. +} +procedure TsCustomSpreadWriter.ListAllNumFormatsCallback(ACell: PCell; AStream: TStream); +begin + FixFormat(ACell); + if FNumFormatList.Find(ACell) = -1 then + FNumFormatList.AddFormat(ACell); +end; + +{@@ + Iterats through all cells and collects the number formats in + FNumFormatList (without duplicates). + The index of the list item is needed for the field FormatIndex of the XF record. } +procedure TsCustomSpreadWriter.ListAllNumFormats; +var + i: Integer; +begin + for i:=0 to Workbook.GetWorksheetCount-1 do + IterateThroughCells(nil, Workbook.GetWorksheetByIndex(i).Cells, ListAllNumFormatsCallback); + NumFormatList.Sort; +end; + {@@ Expands a formula, separating it in it's constituent parts, so that it is already partially parsed and it is easier to diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 7cec8c7ad..b761389aa 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -1,6 +1,10 @@ { Utility functions and constants from FPSpreadsheet } + +// to do: Remove the patched FormatDateTime when the feature of square brackets +// in time format codes is in the rtl + unit fpsutils; {$mode objfpc}{$H+} @@ -59,8 +63,25 @@ function UTF8TextToXMLText(AText: ansistring): ansistring; function TwipsToMillimeters(AValue: Integer): Single; function MillimetersToTwips(AValue: Single): Integer; +function IsExpNumberFormat(s: String; out Decimals: Word; out IsSci: Boolean): Boolean; +function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean; +function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean; +function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean; +function IsDateFormat(s: String; out IsLong: Boolean): Boolean; +function IsTimeFormat(s: String; out isLong, isAMPM, isInterval: Boolean; + out SecDecimals: Word): Boolean; + +function SciFloat(AValue: Double; ADecimals: Word): String; +//function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String; +procedure MakeTimeIntervalMask(Src: String; var Dest: String); + +function FormatDateTime(const FormatStr: string; DateTime: TDateTime): string; + implementation +uses + Math; + { Endianess helper functions @@ -467,5 +488,663 @@ begin Result := Round((AValue * 20 * 72) / 25.4); end; + +{ Format checking procedures } + +{ This simple parsing procedure of the Excel format string checks for a fixed + float format s, i.e. s can be '0', '0.00', '000', '0,000', and returns the + number of decimals, i.e. number of zeros behind the decimal point } +function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean; +var + i: Integer; + p: Integer; + decs: String; +begin + Decimals := 0; + + // Excel time formats with milliseconds ("mm:ss.000") can be incorrectly + // detected as fixed number formats. Check this case at first. + if pos('s.0', s) > 0 then begin + Result := false; + exit; + end; + + // Check if s is a valid format mask. + try + FormatFloat(s, 1.0); + except + on EConvertError do begin + Result := false; + exit; + end; + end; + + // If it is count the zeros - each one is a decimal. + if s = '0' then + Result := true + else begin + p := pos('.', s); // position of decimal point; + if p = 0 then begin + Result := false; + end else begin + Result := true; + for i:= p+1 to Length(s) do + if s[i] = '0' then begin + inc(Decimals) + end + else + exit; // ignore characters after the last 0 + end; + end; +end; + +{ This function checks whether the format string corresponds to a thousand + separator format like "#,##0.000' and returns the number of fixed decimals + (i.e. zeros after the decimal point) } +function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean; +var + i, p: Integer; +begin + Decimals := 0; + + // Check if s is a valid format string + try + FormatFloat(s, 1.0); + except + on EConvertError do begin + Result := false; + exit; + end; + end; + + // If it is look for the thousand separator. If found count decimals. + Result := (Pos(',', s) > 0); + if Result then begin + p := pos('.', s); + if p > 0 then + for i := p+1 to Length(s) do + if s[i] = '0' then + inc(Decimals) + else + exit; // ignore format characters after the last 0 + end; +end; + +{ This function checks whether the format string corresponds to percent + formatting and determines the number of decimals } +function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean; +var + i, p: Integer; +begin + Decimals := 0; + // The signature of the percent format is a percent sign at the end of the + // format string. + Result := (s <> '') and (s[Length(s)] = '%'); + if Result then begin + // Check for a valid format string + try + FormatDateTime(s, 1.0); + except + on EConvertError do begin + Result := false; + exit; + end; + end; + // Count decimals + p := pos('.', s); + if p > 0 then + for i := p+1 to Length(s)-1 do + if s[i] = '0' then + inc(Decimals) + else + exit; // ignore characters after last 0 + end; +end; + +{ This function checks whether the format string corresponds to exponential + formatting and determines the number of decimals. If it contains a # character + the function assumes a "scientific" format rounding the exponent to multiples + of 2. } +function IsExpNumberFormat(s: String; out Decimals: Word; + out IsSci: Boolean): Boolean; +var + i, pdp, pe, ph: Integer; +begin + Result := false; + Decimals := 0; + IsSci := false; + + if SameText(s, 'General') then + exit; + + // Check for a valid format string + try + FormatDateTime(s, 1.0); + except + on EConvertError do begin + exit; + end; + end; + + pe := pos('e', lowercase(s)); + result := pe > 0; + if Result then begin + // The next character must be a "+", "-", or "0" + if (pe = Length(s)) or not (s[pe+1] in ['+', '-', '0']) then begin + Result := false; + exit; + end; + // Count decimals + pdp := pos('.', s); + if (pdp > 0) then begin + if pdp < pe then + for i:=pdp+1 to pe-1 do + if s[i] = '0' then + inc(Decimals) + else + break; // ignore characters after last 0 + end; + // Look for hash signs # as indicator of the "scientific" format + ph := pos('#', s); + if ph > 0 then IsSci := true; + end; +end; + +{ IsDateFormat checks if the format string s corresponds to a date format } +function IsDateFormat(s: String; out IsLong: Boolean): Boolean; +begin + // Day, month, year are separated by a slash + Result := (pos('/', s) > 0); + if Result then + // Check validity of format string + try + FormatDateTime(s, now); + s := Lowercase(s); + isLong := (pos('mmm', s) <> 0) or (pos('mmmm', s) <> 0); + except on EConvertError do + Result := false; + end; +end; + +{ IsTimeFormat checks if the format string s is a time format. isLong is + true if the string contains hours, minutes and seconds (two colons). + isAMPM is true if the string contains "AM/PM", "A/P" or "AMPM". + isInterval is true if the string contains square bracket codes for time intervals. + SecDecimals is the number of decimals for the seconds. } +function IsTimeFormat(s: String; out isLong, isAMPM, isInterval: Boolean; + out SecDecimals: Word): Boolean; +var + p, pdp, i, count: Integer; +begin + isLong := false; + isAMPM := false; + SecDecimals := 0; + + // Time parts are separated by a colon + p := pos(':', s); + result := p > 0; + + if Result then begin + count := 1; + s := Uppercase(s); + + // If there are is a second colon s is a "long" time format + for i:=p+1 to Length(s) do + if s[i] = ':' then begin + isLong := true; + break; + end; + + // Seek for "AM/PM" etc to detect that specific format + isAMPM := (pos('AM/PM', s) > 0) or (pos('A/P', s) > 0) or (pos('AMPM', s) > 0); + + // Look for square brackets indicating the interval format. + p := pos('[', s); + if p > 0 then isInterval := (pos(']', s) > 0) else isInterval := false; + + // Count decimals + pdp := pos('.', s); + if (pdp > 0) then + for i:=pdp+1 to Length(s) do + if (s[i] in ['0', 'z', 'Z']) then + inc(SecDecimals) + else + break; // ignore characters after last 0 + + // Check validity of format string + try + FormatDateTime(s, now); + except on EConvertError do + Result := false; + end; + end; +end; + +{ Formats the number AValue in "scientific" format with the given number of + decimals. "Scientific" is the same as "exponential", but with exponents rounded + to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). } +function SciFloat(AValue: Double; ADecimals: Word): String; +var + m: Double; + ex: Integer; +begin + if AValue = 0 then + Result := '0.0' + else begin + ex := floor(log10(abs(AValue))); // exponent + // round exponent to multiples of 3 + ex := (ex div 3) * 3; + if ex < 0 then dec(ex, 3); + m := AValue * Power(10, -ex); // mantisse + Result := Format('%.*fE%d', [ADecimals, m, ex]); + end; +end; + (* +{ Formats the number AValue as a time string according to the format string. + If the hour part is between square brackets it can be greater than 24 hours. + Dto for the minutes or seconds part, with the higher-value part being added + and no longer being shown explicitly. + Example: + AValue = 1:30:02, FormatStr = "[mm]:ss]" --> "90:02" } +function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String; +var + hrs, mins, secs: Integer; + diff: Double; + h,m,s,z: Word; + ts: String; + fmt: String; + p: Integer; +begin { + fmt := Lowercase(AFormatStr); + p := pos('h]', fmt); + if p > 0 then begin + System.Delete(fmt, 1, p+2); + Result := FormatDateTime(fmt, AValue); + DecodeTime(frac(abs(AValue)), h, m, s, z); + hrs := h + trunc(abs(AValue))*24; + Result := FormatDateTime(fmt, AValue); + end; + for i + p := pos('h + } + ts := DefaultFormatSettings.TimeSeparator; + DecodeTime(frac(abs(AValue)), h, m, s, z); + hrs := h + trunc(abs(AValue))*24; + if z > 499 then inc(s); + if hrs > 0 then + Result := Format('%d%s%.2d%s%.2d', [hrs, ts, m, ts, s]) + else + Result := Format('%d%s%.2d', [m, ts, s]); + if AValue < 0.0 then Result := '-' + Result; +end; + *) +{ Creates a "time interval" format string having the first code identifier + in square brackets. } +procedure MakeTimeIntervalMask(Src: String; var Dest: String); +var + L: TStrings; +begin + L := TStringList.Create; + try + L.StrictDelimiter := true; + L.Delimiter := ':'; + L.DelimitedText := Src; + if L[0][1] <> '[' then L[0] := '[' + L[0]; + if L[0][Length(L[0])] <> ']' then L[0] := L[0] + ']'; + Dest := L.DelimitedText; + finally + L.Free; + end; +end; + + +{******************************************************************************} +{******************************************************************************} +{ Patch for SysUtils.FormatDateTime } +{ Remove when the feature of square brackets in time format masks is in rtl } +{******************************************************************************} +{******************************************************************************} + +// Copied from "fpc/rtl/objpas/sysutils/datei.inc" +procedure DateTimeToString(out Result: string; const FormatStr: string; const DateTime: TDateTime; const FormatSettings: TFormatSettings); +var + ResultLen: integer; + ResultBuffer: array[0..255] of char; + ResultCurrent: pchar; + +{$IFDEF MSWindows} + isEnable_E_Format : Boolean; + isEnable_G_Format : Boolean; + eastasiainited : boolean; +{$ENDIF MSWindows} + +(* This part is in the original code. It is not needed here and avoids a + dependency on the unit Windows. + +{$IFDEF MSWindows} + procedure InitEastAsia; + var ALCID : LCID; + PriLangID , SubLangID : Word; + + begin + ALCID := GetThreadLocale; + PriLangID := ALCID and $3FF; + if (PriLangID>0) then + SubLangID := (ALCID and $FFFF) shr 10 + else + begin + PriLangID := SysLocale.PriLangID; + SubLangID := SysLocale.SubLangID; + end; + isEnable_E_Format := (PriLangID = LANG_JAPANESE) + or + (PriLangID = LANG_KOREAN) + or + ((PriLangID = LANG_CHINESE) + and + (SubLangID = SUBLANG_CHINESE_TRADITIONAL) + ); + isEnable_G_Format := (PriLangID = LANG_JAPANESE) + or + ((PriLangID = LANG_CHINESE) + and + (SubLangID = SUBLANG_CHINESE_TRADITIONAL) + ); + eastasiainited :=true; + end; +{$ENDIF MSWindows} +*) + procedure StoreStr(Str: PChar; Len: Integer); + begin + if ResultLen + Len < SizeOf(ResultBuffer) then + begin + StrMove(ResultCurrent, Str, Len); + ResultCurrent := ResultCurrent + Len; + ResultLen := ResultLen + Len; + end; + end; + + procedure StoreString(const Str: string); + var Len: integer; + begin + Len := Length(Str); + if ResultLen + Len < SizeOf(ResultBuffer) then + begin + StrMove(ResultCurrent, pchar(Str), Len); + ResultCurrent := ResultCurrent + Len; + ResultLen := ResultLen + Len; + end; + end; + + procedure StoreInt(Value, Digits: Integer); + var + S: string[16]; + Len: integer; + begin + System.Str(Value:Digits, S); + for Len := 1 to Length(S) do + begin + if S[Len] = ' ' then + S[Len] := '0' + else + Break; + end; + StoreStr(pchar(@S[1]), Length(S)); + end ; + +var + Year, Month, Day, DayOfWeek, Hour, Minute, Second, MilliSecond: word; + + + procedure StoreFormat(const FormatStr: string; Nesting: Integer; TimeFlag: Boolean); + var + Token, lastformattoken, prevlasttoken: char; + FormatCurrent: pchar; + FormatEnd: pchar; + Count: integer; + Clock12: boolean; + P: pchar; + tmp: integer; + isInterval: Boolean; + + begin + if Nesting > 1 then // 0 is original string, 1 is included FormatString + Exit; + + FormatCurrent := PChar(FormatStr); + FormatEnd := FormatCurrent + Length(FormatStr); + Clock12 := false; + isInterval := false; + P := FormatCurrent; + // look for unquoted 12-hour clock token + while P < FormatEnd do + begin + Token := P^; + case Token of + '''', '"': + begin + Inc(P); + while (P < FormatEnd) and (P^ <> Token) do + Inc(P); + end; + 'A', 'a': + begin + if (StrLIComp(P, 'A/P', 3) = 0) or + (StrLIComp(P, 'AMPM', 4) = 0) or + (StrLIComp(P, 'AM/PM', 5) = 0) then + begin + Clock12 := true; + break; + end; + end; + end; // case + Inc(P); + end ; + token := #255; + lastformattoken := ' '; + prevlasttoken := 'H'; + while FormatCurrent < FormatEnd do + begin + Token := UpCase(FormatCurrent^); + Count := 1; + P := FormatCurrent + 1; + case Token of + '''', '"': + begin + while (P < FormatEnd) and (p^ <> Token) do + Inc(P); + Inc(P); + Count := P - FormatCurrent; + StoreStr(FormatCurrent + 1, Count - 2); + end ; + 'A': + begin + if StrLIComp(FormatCurrent, 'AMPM', 4) = 0 then + begin + Count := 4; + if Hour < 12 then + StoreString(FormatSettings.TimeAMString) + else + StoreString(FormatSettings.TimePMString); + end + else if StrLIComp(FormatCurrent, 'AM/PM', 5) = 0 then + begin + Count := 5; + if Hour < 12 then StoreStr(FormatCurrent, 2) + else StoreStr(FormatCurrent+3, 2); + end + else if StrLIComp(FormatCurrent, 'A/P', 3) = 0 then + begin + Count := 3; + if Hour < 12 then StoreStr(FormatCurrent, 1) + else StoreStr(FormatCurrent+2, 1); + end + else + raise EConvertError.Create('Illegal character in format string'); + end ; + '/': StoreStr(@FormatSettings.DateSeparator, 1); + ':': StoreStr(@FormatSettings.TimeSeparator, 1); + '[': isInterval := true; + ']': isInterval := false; + ' ', 'C', 'D', 'H', 'M', 'N', 'S', 'T', 'Y','Z' : + begin + while (P < FormatEnd) and (UpCase(P^) = Token) do + Inc(P); + Count := P - FormatCurrent; + case Token of + ' ': StoreStr(FormatCurrent, Count); + 'Y': begin + if Count > 2 then + StoreInt(Year, 4) + else + StoreInt(Year mod 100, 2); + end; + 'M': begin + if isInterval and ((prevlasttoken = 'H') or TimeFlag) then + StoreInt(Minute + Hour*60 + trunc(DateTime)*24*60, 0) + else + if (lastformattoken = 'H') or TimeFlag then + begin + if Count = 1 then + StoreInt(Minute, 0) + else + StoreInt(Minute, 2); + end + else + begin + case Count of + 1: StoreInt(Month, 0); + 2: StoreInt(Month, 2); + 3: StoreString(FormatSettings.ShortMonthNames[Month]); + else + StoreString(FormatSettings.LongMonthNames[Month]); + end; + end; + end; + 'D': begin + case Count of + 1: StoreInt(Day, 0); + 2: StoreInt(Day, 2); + 3: StoreString(FormatSettings.ShortDayNames[DayOfWeek]); + 4: StoreString(FormatSettings.LongDayNames[DayOfWeek]); + 5: StoreFormat(FormatSettings.ShortDateFormat, Nesting+1, False); + else + StoreFormat(FormatSettings.LongDateFormat, Nesting+1, False); + end ; + end ; + 'H': + if isInterval then + StoreInt(Hour + trunc(DateTime)*24, 0) + else + if Clock12 then + begin + tmp := hour mod 12; + if tmp=0 then tmp:=12; + if Count = 1 then + StoreInt(tmp, 0) + else + StoreInt(tmp, 2); + end + else begin + if Count = 1 then + StoreInt(Hour, 0) + else + StoreInt(Hour, 2); + end; + 'N': if isInterval then + StoreInt(Minute + 60*Hour + 60*24*trunc(DateTime), 0) + else + if Count = 1 then + StoreInt(Minute, 0) + else + StoreInt(Minute, 2); + 'S': if isInterval then + StoreInt(Second + Minute*60 + Hour*60*60 + trunc(DateTime)*24*60*60, 0) + else + if Count = 1 then + StoreInt(Second, 0) + else + StoreInt(Second, 2); + 'Z': if Count = 1 then + StoreInt(MilliSecond, 0) + else + StoreInt(MilliSecond, 3); + 'T': if Count = 1 then + StoreFormat(FormatSettings.ShortTimeFormat, Nesting+1, True) + else + StoreFormat(FormatSettings.LongTimeFormat, Nesting+1, True); + 'C': begin + StoreFormat(FormatSettings.ShortDateFormat, Nesting+1, False); + if (Hour<>0) or (Minute<>0) or (Second<>0) then + begin + StoreString(' '); + StoreFormat(FormatSettings.LongTimeFormat, Nesting+1, True); + end; + end; + +(* This part is in the original code. It is not needed here and avoids a + dependency on the unit Windows. + +{$IFDEF MSWindows} + 'E': + begin + if not Eastasiainited then InitEastAsia; + if Not(isEnable_E_Format) then StoreStr(@FormatCurrent^, 1) + else + begin + while (P < FormatEnd) and (UpCase(P^) = Token) do + P := P + 1; + Count := P - FormatCurrent; + StoreString(ConvertEraYearString(Count,Year,Month,Day)); + end; + lastformattoken:=token; + end; + 'G': + begin + if not Eastasiainited then InitEastAsia; + if Not(isEnable_G_Format) then StoreStr(@FormatCurrent^, 1) + else + begin + while (P < FormatEnd) and (UpCase(P^) = Token) do + P := P + 1; + Count := P - FormatCurrent; + StoreString(ConvertEraString(Count,Year,Month,Day)); + end; + lastformattoken:=token; + end; +{$ENDIF MSWindows} +*) + end; + prevlasttoken := lastformattoken; + lastformattoken := token; + end; + else + StoreStr(@Token, 1); + end ; + Inc(FormatCurrent, Count); + end; + end; + +begin +{$ifdef MSWindows} + eastasiainited:=false; +{$endif MSWindows} + DecodeDateFully(DateTime, Year, Month, Day, DayOfWeek); + DecodeTime(DateTime, Hour, Minute, Second, MilliSecond); + ResultLen := 0; + ResultCurrent := @ResultBuffer[0]; + if FormatStr <> '' then + StoreFormat(FormatStr, 0, False) + else + StoreFormat('C', 0, False); + ResultBuffer[ResultLen] := #0; + result := StrPas(@ResultBuffer[0]); +end ; + +function FormatDateTime(const FormatStr: string; DateTime: TDateTime): string; +begin + DateTimeToString(Result, FormatStr, DateTime, DefaultFormatSettings); +end; + end. diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index 014d67267..957028f52 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -38,6 +38,15 @@ uses type + { TsBIFF2NumFormatList } + TsBIFF2NumFormatList = class(TsCustomNumFormatList) + protected + procedure AddBuiltinFormats; override; + public + constructor Create; + function FormatStringForWriting(AIndex: Integer): String; override; + end; + { TsSpreadBIFF2Reader } TsSpreadBIFF2Reader = class(TsSpreadBIFFReader) @@ -47,6 +56,7 @@ type FFont: TsFont; protected procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); override; + procedure CreateNumFormatList; override; procedure ExtractNumberFormat(AXFIndex: WORD; out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormatStr: String); override; @@ -80,9 +90,7 @@ type procedure WriteEOF(AStream: TStream); procedure WriteFont(AStream: TStream; AFontIndex: Integer); procedure WriteFonts(AStream: TStream); - procedure WriteFormat(AStream: TStream; AFormatCode: String); procedure WriteFormatCount(AStream: TStream); - procedure WriteFormats(AStream: TStream); procedure WriteIXFE(AStream: TStream; XFIndex: Word); procedure WriteXF(AStream: TStream; AFontIndex, AFormatIndex: byte; ABorders: TsCellBorders = []; AHorAlign: TsHorAlignment = haLeft; @@ -90,12 +98,17 @@ type procedure WriteXFFieldsForFormattingStyles(AStream: TStream); procedure WriteXFRecords(AStream: TStream); protected + procedure CreateNumFormatList; override; + procedure FixFormat(ACell: PCell); override; + procedure ListAllNumFormats; override; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; - procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override; + procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; + AListIndex: Integer); override; procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override; procedure WriteRow(AStream: TStream; ASheet: TsWorksheet; ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); override; + procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override; procedure WriteWindow1(AStream: TStream); override; procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet); public @@ -116,7 +129,8 @@ var $00FFFF // $07: cyan ); - { These are the built-in number formats of BIFF 2. They are not stored in +(* +{ These are the built-in number formats of BIFF2. They are not stored in the file. Note that, compared to the BUFF5+ built-in formats, two formats are missing and the indexes are offset by 2 after #11. It seems that BIFF2 can handle only these 21 formats. The other formats @@ -145,6 +159,7 @@ var 'h:mm:ss', 'M/D/YY h:mm' // 20 ); +*) implementation @@ -177,7 +192,7 @@ const INT_EXCEL_SHEET = $0010; INT_EXCEL_CHART = $0020; INT_EXCEL_MACRO_SHEET = $0040; - + (* { FORMAT record constants for BIFF2 } // Subset of the built-in formats for US Excel, // including those needed for date/time output @@ -206,6 +221,76 @@ const FORMAT_TIME_MS = 19; //time MM:SS FORMAT_TIME_MSZ = 19; //time MM:SS.0 FORMAT_TIME_INTERVAL = 19; //time [hh]:mm:ss, hh can be >24 + *) + +{ TsBIFF2NumFormatList } + +constructor TsBIFF2NumFormatList.Create; +begin + inherited Create; +end; + +procedure TsBIFF2NumFormatList.AddBuiltinFormats; +begin + AddFormat( 0, nfGeneral); + AddFormat( 1, nfFixed, '0', 0); + AddFormat( 2, nfFixed, '0.00', 2); + AddFormat( 3, nfFixedTh, '#,##0', 0); + AddFormat( 4, nfFixedTh, '#,##0.00', 2); + AddFormat( 5, nfFixedTh, '"$"#,##0_);("$"#,##0)', 0); + AddFormat( 6, nfFixedTh, '"$"#,##0_);[Red]("$"#,##0)', 2); + AddFormat( 7, nfFixedTh, '"$"#,##0.00_);("$"#,##0.00)', 0); + AddFormat( 8, nfFixedTh, '"$"#,##0.00_);[Red]("$"#,##0.00)', 2); + AddFormat( 9, nfPercentage, '0%', 0); + AddFormat(10, nfPercentage, '0.00%', 2); + AddFormat(11, nfExp, '0.00E+00', 2); + AddFormat(12, nfShortDate); + AddFormat(13, nfLongDate); + AddFormat(14, nfFmtDateTime, 'd-mmm'); + AddFormat(15, nfFmtDateTime, 'mmm-yy'); + AddFormat(16, nfShortTimeAM); + AddFormat(17, nfLongTimeAM); + AddFormat(18, nfShortTime); + AddFormat(19, nfLongTime); + AddFormat(20, nfShortDateTime); + + FFirstFormatIndexInFile := 0; // BIFF2 stores built-in formats to file. + FNextFormatIndex := 21; // not needed - there are not user-defined formats +end; + +{ Creates formatting strings that are written into the file. } +function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String; +var + ds, ts, cs: string; +begin + ds := DefaultFormatSettings.DecimalSeparator; + ts := DefaultFormatSettings.ThousandSeparator; + cs := DefaultFormatSettings.CurrencyString; + case AIndex of + 0: Result := 'General'; + 1: Result := '0'; + 2: Result := '0' + ds + '00'; // 0.00 + 3: Result := '#' + ts + '#0'; // #,##0 + 4: Result := '#' + ts + '#0' + ds + '0'; // #,##0.00 + 5: Result := UTF8ToAnsi(Format('"%s"#%s##0_);("%s"#%s##0)', [cs, ts, cs, ts])); + 6: Result := UTF8ToAnsi(Format('"%s"#%s##0_);[Red]("%s"#%s##0)', [cs, ts, cs, ts])); + 7: Result := UTF8ToAnsi(Format('"%s"#%s##0%s00_);("%s"#%s##0%s00)', [cs, ts, ds, cs, ts, ds])); + 8: Result := UTF8ToAnsi(Format('"%s"#%s##0%s00_);[Red]("%s"#%s##0%s00)', [cs, ts, ds, cs, ts, ds])); + 9: Result := '0%'; + 10: Result := '0' + ds + '00%'; // 0.00% + 11: Result := '0' + ds + '00E+00'; // 0.00E+00 + 12: Result := 'm/d/yy'; + 13: Result := 'd-mmm-yy'; + 14: Result := 'd-mmm'; + 15: Result := 'mmm-yy'; + 16: Result := 'h:mm AM/PM'; + 17: Result := 'h:mm:ss AM/PM'; + 18: Result := 'h:mm'; + 19: Result := 'h:mm:ss'; + 20: Result := 'm/d/yy h:mm'; + end; +end; + { TsSpreadBIFF2Reader } @@ -252,6 +337,14 @@ begin end; end; +{ Creates the correct version of the number format list. + It is for BIFF2 and BIFF3 file formats. } +procedure TsSpreadBIFF2Reader.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsBIFF2NumFormatList.Create; +end; + { Extracts the number format data from an XF record indexed by AXFIndex. Note that BIFF2 supports only 21 formats. } procedure TsSpreadBIFF2Reader.ExtractNumberFormat(AXFIndex: WORD; @@ -270,7 +363,7 @@ const 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 11..20 ); var - lFormatData: TFormatListData; + lNumFormatData: TsNumFormatData; lXFData: TXFListData; isAMPM: Boolean; isLongTime: Boolean; @@ -280,9 +373,9 @@ begin ANumberFormat := nfGeneral; ANumberFormatStr := ''; ADecimals := 0; - - lFormatData := FindFormatDataForCell(AXFIndex); - if lFormatData = nil then begin + (* + lNumFormatData := FindNumFormatDataForCell(AXFIndex); + if lNumFormatData = nil then begin // no custom format, so first test for default formats lXFData := TXFListData (FXFList.Items[AXFIndex]); if (lXFData.FormatIndex > 0) and (lXFData.FormatIndex <= 20) then begin @@ -291,20 +384,20 @@ begin end; end else // The next is copied from xlscommon - I think it's not necessary here - if IsPercentNumberFormat(lFormatData.FormatString, ADecimals) then + if IsPercentNumberFormat(lNumFormatData.FormatString, ADecimals) then ANumberFormat := nfPercentage else - if IsExpNumberFormat(lFormatData.Formatstring, ADecimals) then + if IsExpNumberFormat(lNumFormatData.Formatstring, ADecimals) then ANumberFormat := nfExp else - if IsThousandSepNumberFormat(lFormatData.FormatString, ADecimals) then + if IsThousandSepNumberFormat(lNumFormatData.FormatString, ADecimals) then ANumberFormat := nfFixedTh else - if IsFixedNumberFormat(lFormatData.FormatString, ADecimals) then + if IsFixedNumberFormat(lNumFormatData.FormatString, ADecimals) then ANumberFormat := nfFixed else begin - t := IsTimeFormat(lFormatData.FormatString, isLongTime, isAMPM, isMilliSec); - d := IsDateFormat(lFormatData.FormatString); + t := IsTimeFormat(lNumFormatData.FormatString, isLongTime, isAMPM, isMilliSec); + d := IsDateFormat(lNumFormatData.FormatString, isLongDate); if d and t then ANumberFormat := nfShortDateTime else @@ -324,7 +417,7 @@ begin ANumberFormat := nfShortTime; end; end; - end; + end; *) end; procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream); @@ -696,6 +789,14 @@ end; { TsSpreadBIFF2Writer } +{ Creates the correct version of the number format list. + It is for BIFF2 and BIFF3 file formats. } +procedure TsSpreadBIFF2Writer.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsBIFF2NumFormatList.Create; +end; + function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word; var i: Integer; @@ -712,6 +813,37 @@ begin end; end; +procedure TsSpreadBIFF2Writer.FixFormat(ACell: PCell); +var + j: Integer; +begin + case ACell.NumberFormat of + nfExp: + if ACell.NumberDecimals <> 2 then begin + ACell.NumberDecimals := 2; + ACell.NumberFormatStr := '0.00E+00'; + end; + nfSci: + begin + ACell.NumberFormat := nfExp; + ACell.NumberFormatStr := '0.00E+00'; + ACell.NumberDecimals := 2; + end; + nfFmtDateTime: + begin + j := NumFormatList.Find(ACell); + if j = -1 then ACell.NumberFormat := nfLongTime; + end; + nfCustom: + ACell.NumberFormat := nfGeneral; + end; +end; + +procedure TsSpreadBIFF2Writer.ListAllNumFormats; +begin + // Nothing to do. All formats have already been added by the NumFormatList. +end; + { Attaches cell formatting data for the given cell to the current record. Is called from all writing methods of cell contents. @@ -825,6 +957,7 @@ begin WriteBOF(AStream); WriteFonts(AStream); + WriteFormatCount(AStream); WriteFormats(AStream); WriteXFRecords(AStream); WriteColWidths(AStream); @@ -940,7 +1073,7 @@ end; procedure TsSpreadBIFF2Writer.WriteXFFieldsForFormattingStyles(AStream: TStream); var - i: Integer; + i, j: Integer; lFontIndex: Word; lFormatIndex: Word; //number format lBorders: TsCellBorders; @@ -958,66 +1091,19 @@ begin lHorAlign := FFormattingStyles[i].HorAlignment; // Now apply the modifications. - if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then - case FFormattingStyles[i].NumberFormat of - nfFixed: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_2_DECIMALS; - end; - nfFixedTh: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_THOUSANDS_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_THOUSANDS_2_DECIMALS; - end; - nfExp: - lFormatIndex := FORMAT_EXP_2_DECIMALS; - nfSci: - lFormatIndex := FORMAT_SCI_1_DECIMAL; - nfPercentage: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_PERCENT_0_DECIMALS; - 2: lFormatIndex := FORMAT_PERCENT_2_DECIMALS; - end; + if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin + j := NumFormatList.Find(@FFormattingStyles[i]); + if j > -1 then begin + lFormatIndex := NumFormatList[j].Index; { - nfCurrency: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_CURRENCY_0_DECIMALS; - 2: lFormatIndex := FORMAT_CURRENCY_2_DECIMALS; - end; - } - nfShortDate: - lFormatIndex := FORMAT_SHORT_DATE; - nfShortTime: - lFormatIndex := FORMAT_SHORT_TIME; - nfLongTime: - lFormatIndex := FORMAT_LONG_TIME; - nfShortTimeAM: - lFormatIndex := FORMAT_SHORT_TIME_AM; - nfLongTimeAM: - lFormatIndex := FORMAT_LONG_TIME_AM; - nfShortDateTime: - lFormatIndex := FORMAT_SHORT_DATETIME; - nfFmtDateTime: - begin - fmt := lowercase(FFormattingStyles[i].NumberFormatStr); - if (fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm') then - lFormatIndex := FORMAT_DATE_DM - else - if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then - lFormatIndex := FORMAT_DATE_MY - else - { Because of limitations of BIFF2 the next two formats are mapped - to the same format index! } - if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then - lFormatIndex := FORMAT_TIME_MS - else - if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then - lFormatIndex := FORMAT_TIME_MSZ; - end; - nfTimeInterval: - lFormatIndex := FORMAT_TIME_INTERVAL; + // BIFF2 can only handle the 21 built-in formats. Here we find replacements + // for the others. + case NumFormatList[j].NumFormat of + nfSci : lFormatIndex := 11; // Exp + nfFmtDateTime : if lFormatIndex > 20 then lFormatIndex := 19; + end;} end; + end; if uffBorder in FFormattingStyles[i].UsedFormattingFields then lBorders := FFormattingStyles[i].Border; @@ -1149,6 +1235,24 @@ begin WriteFont(AStream, i); end; +procedure TsSpreadBiff2Writer.WriteFormat(AStream: TStream; + AFormatData: TsNumFormatData; AListIndex: Integer); +var + len: Integer; + s: ansistring; +begin + s := NumFormatList.FormatStringForWriting(AListIndex); + len := Length(s); + + { BIFF Record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); + AStream.WriteWord(WordToLE(1 + len)); + + { Format string } + AStream.WriteByte(len); // AnsiString, char count in 1 byte + AStream.WriteBuffer(s[1], len); // String data +end; + (* procedure TsSpreadBIFF2Writer.WriteFormat(AStream: TStream; AFormatCode: String); var len: Integer; @@ -1167,15 +1271,15 @@ begin { Write format string } AStream.WriteByte(len); AStream.WriteBuffer(s[1], len); -end; +end; *) procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream); begin AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMATCOUNT)); AStream.WriteWord(WordToLE(2)); - AStream.WriteWord(WordToLE(High(NUMFORMAT_BIFF2)+1)); + AStream.WriteWord(WordToLE(21)); // there are 21 built-in formats end; - + (* procedure TsSpreadBIFF2Writer.WriteFormats(AStream: TStream); var i: Integer; @@ -1183,7 +1287,7 @@ begin WriteFormatCount(AStream); for i:=0 to High(NUMFORMAT_BIFF2) do WriteFormat(AStream, NUMFORMAT_BIFF2[i]); -end; +end;*) (* var ds, ts: Char; //decimal separator, thousand separator diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 51e217045..df73fe1e7 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -84,8 +84,6 @@ type procedure ReadBlank(AStream: TStream); override; procedure ReadFont(const AStream: TStream); procedure ReadFormat(AStream: TStream); override; - procedure ReadFormula(AStream: TStream); override; - procedure ReadFormulaExcel(AStream: TStream); procedure ReadLabel(AStream: TStream); override; procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook); procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); @@ -115,6 +113,8 @@ type procedure WriteEOF(AStream: TStream); procedure WriteFont(AStream: TStream; AFont: TsFont); procedure WriteFonts(AStream: TStream); + procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; + AListIndex: Integer); override; procedure WriteIndex(AStream: TStream); procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); override; @@ -130,6 +130,7 @@ type procedure WriteXFFieldsForFormattingStyles(AStream: TStream); procedure WriteXFRecords(AStream: TStream); public + constructor Create(AWorkbook: TsWorkbook); override; { General writing methods } procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); override; @@ -296,9 +297,21 @@ const MASK_XF_BKGR_BACKGROUND_COLOR = $00003F80; MASK_XF_BKGR_FILLPATTERN = $003F0000; + TEXT_ROTATIONS: Array[TsTextRotation] of Byte = ( + XF_ROTATION_HORIZONTAL, + XF_ROTATION_90DEG_CW, + XF_ROTATION_90DEG_CCW, + XF_ROTATION_STACKED + ); + { TsSpreadBIFF5Writer } +constructor TsSpreadBIFF5Writer.Create(AWorkbook: TsWorkbook); +begin + inherited Create(AWorkbook); +end; + {******************************************************************* * TsSpreadBIFF5Writer.WriteToFile () * @@ -362,6 +375,7 @@ begin WriteCodepage(AStream, WorkBookEncoding); WriteWindow1(AStream); WriteFonts(AStream); + WriteFormats(AStream); WritePalette(AStream); WriteXFRecords(AStream); WriteStyle(AStream); @@ -632,6 +646,36 @@ begin WriteFont(AStream, Workbook.GetFont(i)); end; +{******************************************************************* +* TsSpreadBIFF5Writer.WriteFormat +* +* DESCRIPTION: Writes an Excel 5 FORMAT record +* +*******************************************************************} +procedure TsSpreadBiff5Writer.WriteFormat(AStream: TStream; + AFormatData: TsNumFormatData; AListIndex: Integer); +var + len: Integer; + s: ansistring; +begin + if (AFormatData = nil) or (AFormatData.FormatString = '') then + exit; + + s := NumFormatList.FormatStringForWriting(AListIndex); + len := Length(s); + + { BIFF Record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); + AStream.WriteWord(WordToLE(2 + 1 + len * SizeOf(AnsiChar))); + + { Format index } + AStream.WriteWord(WordToLE(AFormatData.Index)); + + { Format string } + AStream.WriteByte(len); // AnsiString, char count in 1 byte + AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data +end; + {******************************************************************* * TsSpreadBIFF5Writer.WriteRPNFormula () * @@ -1047,7 +1091,7 @@ end; procedure TsSpreadBIFF5Writer.WriteXFFieldsForFormattingStyles(AStream: TStream); var - i: Integer; + i, j: Integer; lFontIndex: Word; lFormatIndex: Word; //number format lTextRotation: Byte; @@ -1073,77 +1117,17 @@ begin lBackgroundColor := FFormattingStyles[i].BackgroundColor; // Now apply the modifications. - if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then - case FFormattingStyles[i].NumberFormat of - nfFixed: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_2_DECIMALS; - end; - nfFixedTh: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_THOUSANDS_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_THOUSANDS_2_DECIMALS; - end; - nfExp: - lFormatIndex := FORMAT_EXP_2_DECIMALS; - nfSci: - lFormatIndex := FORMAT_SCI_1_DECIMAL; - nfPercentage: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_PERCENT_0_DECIMALS; - 2: lFormatIndex := FORMAT_PERCENT_2_DECIMALS; - end; - { - nfCurrency: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_CURRENCY_0_DECIMALS; - 2: lFormatIndex := FORMAT_CURRENCY_2_DECIMALS; - end; - } - nfShortDate: - lFormatIndex := FORMAT_SHORT_DATE; - nfShortTime: - lFormatIndex := FORMAT_SHORT_TIME; - nfLongTime: - lFormatIndex := FORMAT_LONG_TIME; - nfShortTimeAM: - lFormatIndex := FORMAT_SHORT_TIME_AM; - nfLongTimeAM: - lFormatIndex := FORMAT_LONG_TIME_AM; - nfShortDateTime: - lFormatIndex := FORMAT_SHORT_DATETIME; - nfFmtDateTime: - begin - fmt := lowercase(FFormattingStyles[i].NumberFormatStr); - if (fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm') then - lFormatIndex := FORMAT_DATE_DM - else - if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then - lFormatIndex := FORMAT_DATE_MY - else - if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then - lFormatIndex := FORMAT_TIME_MS - else - if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then - lFormatIndex := FORMAT_TIME_MSZ - end; - nfTimeInterval: - lFormatIndex := FORMAT_TIME_INTERVAL; - end; + if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin + j := NumFormatList.Find(@FFormattingStyles[i]); + if j > -1 then + lFormatIndex := NumFormatList[j].Index; + end; if uffBorder in FFormattingStyles[i].UsedFormattingFields then lBorders := FFormattingStyles[i].Border; if uffTextRotation in FFormattingStyles[i].UsedFormattingFields then - begin - case FFormattingStyles[i].TextRotation of - trHorizontal : lTextRotation := XF_ROTATION_HORIZONTAL; - rt90DegreeClockwiseRotation : lTextRotation := XF_ROTATION_90DEG_CW; - rt90DegreeCounterClockwiseRotation : lTextRotation := XF_ROTATION_90DEG_CCW; - rtStacked : lTextRotation := XF_ROTATION_STACKED; - end; - end; + lTextRotation := TEXT_ROTATIONS[FFormattingStyles[i].TextRotation]; if uffBold in FFormattingStyles[i].UsedFormattingFields then lFontIndex := 1; // must be before uffFont which overrides uffBold @@ -1270,7 +1254,7 @@ begin INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); INT_EXCEL_ID_ROW : ReadRowInfo(AStream); - INT_EXCEL_ID_FORMULA : ReadFormulaExcel(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); INT_EXCEL_ID_PANE : ReadPane(AStream); INT_EXCEL_ID_BOF : ; @@ -1376,32 +1360,6 @@ begin ApplyCellFormatting(ARow, ACol, XF); end; -procedure TsSpreadBIFF5Reader.ReadFormulaExcel(AStream: TStream); -var - ARow, ACol: Cardinal; - XF: WORD; - ResultFormula: Double; - Data: array [0..7] of BYTE; - Flags: WORD; - FormulaSize: BYTE; -begin - ReadRowColXF(AStream, ARow, ACol, XF); - - AStream.ReadBuffer(Data,Sizeof(Data)); - Flags:=WordLEtoN(AStream.ReadWord); - AStream.ReadDWord; //Not used. - FormulaSize:=AStream.ReadByte; - //RPN data not used by now - AStream.Position:=AStream.Position+FormulaSize; - - if SizeOf(Double)<>8 then Raise Exception.Create('Double is not 8 bytes'); - Move(Data[0],ResultFormula,sizeof(Data)); - FWorksheet.WriteNumber(ARow, ACol, ResultFormula); - - { Add attributes to cell } - ApplyCellFormatting(ARow, ACol, XF); -end; - procedure TsSpreadBIFF5Reader.ReadFromFile(AFileName: string; AData: TsWorkbook); var MemStream: TMemoryStream; @@ -1638,12 +1596,10 @@ end; // Read the FORMAT record for formatting numerical data procedure TsSpreadBIFF5Reader.ReadFormat(AStream: TStream); var - lData: TFormatListData; - str: AnsiString; len: byte; + fmtIndex: Integer; + fmtString: AnsiString; begin - lData := TFormatListData.Create; - // Record FORMAT, BIFF 8 (5.49): // Offset Size Contents // 0 2 Format index used in other records @@ -1651,21 +1607,15 @@ begin // From BIFF5 on: indexes 0..163 are built in // format index - lData.Index := WordLEtoN(AStream.ReadWord); + fmtIndex := WordLEtoN(AStream.ReadWord); // number format string len := AStream.ReadByte; - SetLength(str, len); - AStream.ReadBuffer(str[1], len); - lData.FormatString := str; + SetLength(fmtString, len); + AStream.ReadBuffer(fmtString[1], len); // Add to the list - FFormatList.Add(lData); -end; - -procedure TsSpreadBIFF5Reader.ReadFormula(AStream: TStream); -begin - + NumFormatList.AnalyzeAndAdd(fmtIndex, fmtString); end; procedure TsSpreadBIFF5Reader.ReadLabel(AStream: TStream); diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index e2bac5d07..bd73afa1d 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -89,7 +89,6 @@ type procedure ReadFormat(AStream: TStream); override; { Record reading methods } procedure ReadBlank(AStream: TStream); override; - procedure ReadFormula(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override; procedure ReadRichString(const AStream: TStream); public @@ -115,6 +114,8 @@ type procedure WriteEOF(AStream: TStream); procedure WriteFont(AStream: TStream; AFont: TsFont); procedure WriteFonts(AStream: TStream); + procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; + AListIndex: Integer); override; procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); override; procedure WriteIndex(AStream: TStream); @@ -131,6 +132,7 @@ type AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver); procedure WriteXFRecords(AStream: TStream); public + constructor Create(AWorkbook: TsWorkbook); override; { General writing methods } procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); override; @@ -263,12 +265,24 @@ const MASK_XF_BORDER_TOP_COLOR = $0000007F; MASK_XF_BORDER_BOTTOM_COLOR = $00003F80; + TEXT_ROTATIONS: Array[TsTextRotation] of Byte = ( + XF_ROTATION_HORIZONTAL, + XF_ROTATION_90DEG_CW, + XF_ROTATION_90DEG_CCW, + XF_ROTATION_STACKED + ); + { TsSpreadBIFF8Writer } +constructor TsSpreadBIFF8Writer.Create(AWorkbook: TsWorkbook); +begin + inherited Create(AWorkbook); +end; + procedure TsSpreadBIFF8Writer.WriteXFFieldsForFormattingStyles(AStream: TStream); var - i: Integer; + i, j: Integer; lFontIndex: Word; lFormatIndex: Word; //number format lTextRotation: Byte; @@ -281,7 +295,7 @@ var lWordWrap: Boolean; fmt: String; begin - // The first style was already added + // The first style was already added --> begin loop with 1 for i := 1 to Length(FFormattingStyles) - 1 do begin // Default styles lFontIndex := 0; @@ -294,77 +308,17 @@ begin lBackgroundColor := FFormattingStyles[i].BackgroundColor; // Now apply the modifications. - if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then - case FFormattingStyles[i].NumberFormat of - nfFixed: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_2_DECIMALS; - end; - nfFixedTh: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_FIXED_THOUSANDS_0_DECIMALS; - 2: lFormatIndex := FORMAT_FIXED_THOUSANDS_2_DECIMALS; - end; - nfExp: - lFormatIndex := FORMAT_EXP_2_DECIMALS; - nfSci: - lFormatIndex := FORMAT_SCI_1_DECIMAL; - nfPercentage: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_PERCENT_0_DECIMALS; - 2: lFormatIndex := FORMAT_PERCENT_2_DECIMALS; - end; - { - nfCurrency: - case FFormattingStyles[i].NumberDecimals of - 0: lFormatIndex := FORMAT_CURRENCY_0_DECIMALS; - 2: lFormatIndex := FORMAT_CURRENCY_2_DECIMALS; - end; - } - nfShortDate: - lFormatIndex := FORMAT_SHORT_DATE; - nfShortTime: - lFormatIndex := FORMAT_SHORT_TIME; - nfLongTime: - lFormatIndex := FORMAT_LONG_TIME; - nfShortTimeAM: - lFormatIndex := FORMAT_SHORT_TIME_AM; - nfLongTimeAM: - lFormatIndex := FORMAT_LONG_TIME_AM; - nfShortDateTime: - lFormatIndex := FORMAT_SHORT_DATETIME; - nfFmtDateTime: - begin - fmt := lowercase(FFormattingStyles[i].NumberFormatStr); - if (fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm') then - lFormatIndex := FORMAT_DATE_DM - else - if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then - lFormatIndex := FORMAT_DATE_MY - else - if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then - lFormatIndex := FORMAT_TIME_MS - else - if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then - lFormatIndex := FORMAT_TIME_MSZ - end; - nfTimeInterval: - lFormatIndex := FORMAT_TIME_INTERVAL; - end; + if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin + j := NumFormatList.Find(@FFormattingStyles[i]); + if j > -1 then + lFormatIndex := NumFormatList[j].Index; + end; if uffBorder in FFormattingStyles[i].UsedFormattingFields then lBorders := FFormattingStyles[i].Border; if uffTextRotation in FFormattingStyles[i].UsedFormattingFields then - begin - case FFormattingStyles[i].TextRotation of - trHorizontal: lTextRotation := XF_ROTATION_HORIZONTAL; - rt90DegreeClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CW; - rt90DegreeCounterClockwiseRotation: lTextRotation := XF_ROTATION_90DEG_CCW; - rtStacked: lTextRotation := XF_ROTATION_STACKED; - end; - end; + lTextRotation := TEXT_ROTATIONS[FFormattingStyles[i].TextRotation]; if uffBold in FFormattingStyles[i].UsedFormattingFields then lFontIndex := 1; // must be before uffFont which overrides uffBold @@ -445,6 +399,7 @@ begin WriteWindow1(AStream); WriteFonts(AStream); + WriteFormats(AStream); WritePalette(AStream); WriteXFRecords(AStream); WriteStyle(AStream); @@ -713,6 +668,34 @@ begin WriteFont(AStream, Workbook.GetFont(i)); end; +procedure TsSpreadBiff8Writer.WriteFormat(AStream: TStream; + AFormatData: TsNumFormatData; AListIndex: Integer); +var + len: Integer; + s: widestring; +begin + if (AFormatData = nil) or (AFormatData.FormatString = '') then + exit; + + s := NumFormatList.FormatStringForWriting(AListIndex); + len := Length(s); + + { BIFF Record header } + AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT)); + AStream.WriteWord(WordToLE(2 + 2 + 1 + len * SizeOf(WideChar))); + + { Format index } + AStream.WriteWord(WordToLE(AFormatData.Index)); + + { Format string } + { - Unicodestring, char count in 2 bytes } + AStream.WriteWord(WordToLE(len)); + { - Widestring flags, 1=regular unicode LE string } + AStream.WriteByte(1); + { - String data } + AStream.WriteBuffer(WideStringToLE(s)[1], len * Sizeof(WideChar)); +end; + {******************************************************************* * TsSpreadBIFF8Writer.WriteFormula () * @@ -1435,16 +1418,16 @@ begin if RecordType <> INT_EXCEL_ID_CONTINUE then begin case RecordType of - INT_EXCEL_ID_BOF: ; + INT_EXCEL_ID_BOF : ; INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet(AStream); - INT_EXCEL_ID_EOF: SectionEOF := True; - INT_EXCEL_ID_SST: ReadSST(AStream); - INT_EXCEL_ID_CODEPAGE: ReadCodepage(AStream); - INT_EXCEL_ID_FONT: ReadFont(AStream); - INT_EXCEL_ID_XF: ReadXF(AStream); - INT_EXCEL_ID_FORMAT: ReadFormat(AStream); - INT_EXCEL_ID_DATEMODE: ReadDateMode(AStream); - INT_EXCEL_ID_PALETTE: ReadPalette(AStream); + INT_EXCEL_ID_EOF : SectionEOF := True; + INT_EXCEL_ID_SST : ReadSST(AStream); + INT_EXCEL_ID_CODEPAGE : ReadCodepage(AStream); + INT_EXCEL_ID_FONT : ReadFont(AStream); + INT_EXCEL_ID_FORMAT : ReadFormat(AStream); + INT_EXCEL_ID_XF : ReadXF(AStream); + INT_EXCEL_ID_DATEMODE : ReadDateMode(AStream); + INT_EXCEL_ID_PALETTE : ReadPalette(AStream); else // nothing end; @@ -1624,51 +1607,6 @@ begin ApplyCellFormatting(ARow, ACol, XF); end; -procedure TsSpreadBIFF8Reader.ReadFormula(AStream: TStream); -var - ARow, ACol: Cardinal; - XF: WORD; - ResultFormula: Double; - Data: array [0..7] of BYTE; - Flags: WORD; - FormulaSize: BYTE; - i: Integer; -begin - { BIFF Record header } - { BIFF Record data } - { Index to XF Record } - ReadRowColXF(AStream, ARow, ACol, XF); - - { Result of the formula in IEE 754 floating-point value } - AStream.ReadBuffer(Data, Sizeof(Data)); - - { Options flags } - Flags := WordLEtoN(AStream.ReadWord); - - { Not used } - AStream.ReadDWord; - - { Formula size } - FormulaSize := WordLEtoN(AStream.ReadWord); - - { Formula data, output as debug info } -{ Write('Formula Element: '); - for i := 1 to FormulaSize do - Write(IntToHex(AStream.ReadByte, 2) + ' '); - WriteLn('');} - - //RPN data not used by now - AStream.Position := AStream.Position + FormulaSize; - - if SizeOf(Double) <> 8 then - raise Exception.Create('Double is not 8 bytes'); - Move(Data[0], ResultFormula, SizeOf(Data)); - FWorksheet.WriteNumber(ARow, ACol, ResultFormula); - - {Add attributes} - ApplyCellFormatting(ARow, ACol, XF); -end; - procedure TsSpreadBIFF8Reader.ReadLabel(AStream: TStream); var L: Word; @@ -1975,22 +1913,21 @@ end; // Read the FORMAT record for formatting numerical data procedure TsSpreadBIFF8Reader.ReadFormat(AStream: TStream); var - lData: TFormatListData; + fmtString: String; + fmtIndex: Integer; begin - lData := TFormatListData.Create; - // Record FORMAT, BIFF 8 (5.49): // Offset Size Contents // 0 2 Format index used in other records // 2 var Number format string (Unicode string, 16-bit string length) // From BIFF5 on: indexes 0..163 are built in - lData.Index := WordLEtoN(AStream.ReadWord); + fmtIndex := WordLEtoN(AStream.ReadWord); // 2 var. Number format string (Unicode string, 16-bit string length, ➜2.5.3) - lData.FormatString := ReadWideString(AStream, False); + fmtString := ReadWideString(AStream, False); // Add to the list - FFormatList.Add(lData); + NumFormatList.AnalyzeAndAdd(fmtIndex, fmtString); end; diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 53e22a4bb..66cd7b6fe 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -236,7 +236,7 @@ const { DATEMODE record, 5.28 } DATEMODE_1900_BASE=1; //1/1/1900 minus 1 day in FPC TDateTime DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime - + (* { FORMAT record constants for BIFF5-BIFF8} // Subset of the built-in formats for US Excel, // including those needed for date/time output @@ -262,6 +262,7 @@ const FORMAT_TIME_INTERVAL = 46; //time [hh]:mm:ss, hh can be >24 FORMAT_TIME_MSZ = 47; //time MM:SS.0 FORMAT_SCI_1_DECIMAL = 48; //scientific, 1 decimal + *) { WINDOW1 record constants - BIFF5-BIFF8 } MASK_WINDOW1_OPTION_WINDOW_HIDDEN = $0001; @@ -361,11 +362,12 @@ type BackgroundColor: TsColor; end; - { Contents of the format record for BIFF5/8 } - TFormatListData = class + { TsBIFFNumFormatList } + TsBIFFNumFormatList = class(TsCustomNumFormatList) + protected + procedure AddBuiltinFormats; override; public - Index: Integer; - FormatString: widestring; + function FormatStringForWriting(AIndex: Integer): String; override; end; { TsSpreadBIFFReader } @@ -376,8 +378,8 @@ type FDateMode: TDateMode; FPaletteFound: Boolean; FXFList: TFPList; // of TXFListData - FFormatList: TFPList; // of TFormatListData procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); virtual; + procedure CreateNumFormatList; override; // Extracts a number out of an RK value function DecodeRKValue(const ARK: DWORD): Double; // Returns the numberformat for a given XF record @@ -386,7 +388,7 @@ type out ANumberFormatStr: String); virtual; // Finds format record for XF record pointed to by cell // Will not return info for built-in formats - function FindFormatDataForCell(const AXFIndex: Integer): TFormatListData; + function FindNumFormatDataForCell(const AXFIndex: Integer): TsNumFormatData; // Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): Boolean; // Here we can add reading of records which didn't change across BIFF5-8 versions @@ -397,6 +399,8 @@ type procedure ReadDateMode(AStream: TStream); // Read FORMAT record (cell formatting) procedure ReadFormat(AStream: TStream); virtual; + // Read FORMULA record + procedure ReadFormula(AStream: TStream); override; // Read multiple blank cells procedure ReadMulBlank(AStream: TStream); // Read multiple RK cells @@ -428,6 +432,7 @@ type FLastRow: Integer; FLastCol: Word; procedure AddDefaultFormats; override; + procedure CreateNumFormatList; override; procedure GetLastRowCallback(ACell: PCell; AStream: TStream); function GetLastRowIndex(AWorksheet: TsWorksheet): Integer; procedure GetLastColCallback(ACell: PCell; AStream: TStream); @@ -453,6 +458,11 @@ type // Writes out a TIME/DATE/TIMETIME procedure WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal; const AValue: TDateTime; ACell: PCell); override; + // Writes out a FORMAT record + procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; + AListIndex: Integer); virtual; + // Writes out all FORMAT records + procedure WriteFormats(AStream: TStream); // Writes out a floating point NUMBER record procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: Double; ACell: PCell); override; @@ -478,17 +488,38 @@ type destructor Destroy; override; end; -function IsExpNumberFormat(s: String; out Decimals: Word): Boolean; -function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean; -function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean; -function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean; - -function IsDateFormat(s: String): Boolean; -function IsTimeFormat(s: String; out isLong, isAMPM, isMillisec: Boolean): Boolean; - implementation +uses + StrUtils; + +const + { see ➜ 5.49 } + COUNT_DEFAULT_FORMATS = 58; + NOT_USED = nfGeneral; + DEFAULT_NUM_FORMATS: array[1..COUNT_DEFAULT_FORMATS] of TsNumberFormat = ( + nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5 + nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10 + nfExp, NOT_USED, NOT_USED, nfShortDate, nfShortDate, // 11..15 + nfFmtDateTime, nfFmtDateTime, nfShortTimeAM, nfLongTimeAM, nfShortTime, // 16..20 + nfLongTime, nfShortDateTime, NOT_USED, NOT_USED, NOT_USED, // 21..25 + NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 26..30 + NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 31..35 + NOT_USED, nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, // 36..40 + nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, nfFmtDateTime, // 41..45 + nfTimeInterval, nfFmtDateTime, nfSci, NOT_USED, NOT_USED, // 46..50 + NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 51..55 + NOT_USED, NOT_USED, NOT_USED // 56..58 + ); + DEFAULT_NUM_FORMAT_DECIMALS: array[1..COUNT_DEFAULT_FORMATS] of word = ( + 0, 2, 0, 2, 0, 0, 2, 2, 0, 2, // 1..10 + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 11..20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21..30 + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, // 31..40 + 0, 0, 2, 2, 0, 3, 0, 1, 0, 0, // 41..50 #48 is "scientific", use "exponential" instead + 0, 0, 0, 0, 0, 0, 0, 0); // 51..58 + function ConvertExcelDateTimeToDateTime( const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime; begin @@ -540,13 +571,75 @@ begin end; +{ TsBIFFNumFormatList } + +{ These are the built-in number formats as used by fpc. Before writing to file + some code will be modified to become compatible with Excel + (--> FormatStringForWriting) } +procedure TsBIFFNumFormatList.AddBuiltinFormats; +begin + AddFormat( 0, nfGeneral); + AddFormat( 1, nfFixed, '0', 0); + AddFormat( 2, nfFixed, '0.00', 2); + AddFormat( 3, nfFixedTh, '#,##0', 0); + AddFormat( 4, nfFixedTh, '#,##0.00', 2); + // 5..8 currently not supported + AddFormat( 9, nfPercentage, '0%', 0); + AddFormat(10, nfPercentage, '0.00%', 2); + AddFormat(11, nfExp, '0.00E+00', 2); + // fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported + AddFormat(14, nfShortDate); + AddFormat(15, nfLongDate); + AddFormat(16, nfFmtDateTime, 'D-MMM'); + AddFormat(17, nfFmtDateTime, 'MMM-YY'); + AddFormat(18, nfShortTimeAM); + AddFormat(19, nfLongTimeAM); + AddFormat(20, nfShortTime); + AddFormat(21, nfLongTime); + AddFormat(22, nfShortDateTime); + // 23..44 not supported + AddFormat(45, nfFmtDateTime, 'mm:ss'); + AddFormat(46, nfTimeInterval, '[h]:mm:ss'); + AddFormat(47, nfFmtDateTime, 'mm:ss.z'); // z will be replace by 0 later + AddFormat(48, nfSci, '##0.0E+0', 1); + // 49 ("Text") not supported + + // All indexes from 0 to 163 are reserved for built-in formats. + // The first user-defined format starts at 164. + FFirstFormatIndexInFile := 164; + FNextFormatIndex := 164; +end; + +{ Creates formatting strings that are written into the file. } +function TsBIFFNumFormatList.FormatStringForWriting(AIndex: Integer): String; +var + item: TsNumFormatData; + i: Integer; +begin + Result := inherited FormatStringForWriting(AIndex); + item := Items[AIndex]; + case item.NumFormat of + nfFmtDateTime: + begin + Result := lowercase(item.FormatString); + for i:=1 to Length(Result) do + if Result[i] in ['z', 'Z'] then Result[i] := '0'; + end; + nfTimeInterval: + // Time interval format string could still be without square brackets + // if added by user. + // We check here for safety and add the brackets if not there. + MakeTimeIntervalMask(item.FormatString, Result); + end; +end; + + { TsSpreadBIFFReader } constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook); begin inherited Create(AWorkbook); FXFList := TFPList.Create; - FFormatList := TFPList.Create; // Initial base date in case it won't be read from file FDateMode := dm1900; end; @@ -557,8 +650,6 @@ var begin for j := FXFList.Count-1 downto 0 do TObject(FXFList[j]).Free; FXFList.Free; - for j := FFormatList.Count-1 downto 0 do TObject(FFormatList[j]).Free; - FFormatList.Free; inherited Destroy; end; @@ -614,6 +705,15 @@ begin end; end; +{ Creates the correct version of the number format list. It is for BIFF file + formats. + Valid for BIFF5.BIFF8. Needs to be overridden for BIFF2. } +procedure TsSpreadBIFFReader.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsBIFFNumFormatList.Create; +end; + { Extracts a number out of an RK value. Valid since BIFF3. } function TsSpreadBIFFReader.DecodeRKValue(const ARK: DWORD): Double; @@ -651,120 +751,51 @@ end; procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD; out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormatStr: String); -const - { see ➜ 5.49 } - NOT_USED = nfGeneral; - fmts: array[1..58] of TsNumberFormat = ( - nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5 - nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10 - nfExp, NOT_USED, NOT_USED, nfShortDate, nfShortDate, // 11..15 - nfFmtDateTime, nfFmtDateTime, nfShortTimeAM, nfLongTimeAM, nfShortTime, // 16..20 - nfLongTime, nfShortDateTime, NOT_USED, NOT_USED, NOT_USED, // 21..25 - NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 26..30 - NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 31..35 - NOT_USED, nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, // 36..40 - nfFixedTh, nfFixedTh, nfFixedTh, nfFixedTh, nfFmtDateTime, // 41..45 - nfTimeInterval, nfFmtDateTime, nfSci, NOT_USED, NOT_USED, // 46..50 - NOT_USED, NOT_USED, NOT_USED, NOT_USED, NOT_USED, // 51..55 - NOT_USED, NOT_USED, NOT_USED // 56..58 - ); - decs: array[1..58] of word = ( - 0, 2, 0, 2, 0, 0, 2, 2, 0, 2, // 1..10 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 11..20 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21..30 - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, // 31..40 - 0, 0, 2, 2, 0, 3, 0, 1, 0, 0, // 41..50 #48 is "scientific", use "exponential" instead - 0, 0, 0, 0, 0, 0, 0, 0); // 51..58 -var - lFormatData: TFormatListData; - lXFData: TXFListData; - isAMPM: Boolean; - isLongTime: Boolean; - isMilliSec: Boolean; - t,d: Boolean; -begin - ANumberFormat := nfGeneral; - ANumberFormatStr := ''; - ADecimals := 0; - lFormatData := FindFormatDataForCell(AXFIndex); - {Record FORMAT, BIFF 5/8 (5.49): - Offset Size Contents - 0 2 Format index used in other records - } - - if lFormatData = nil then begin - // no custom format, so first test for default formats - lXFData := TXFListData(FXFList.Items[AXFIndex]); - case lXFData.FormatIndex of - FORMAT_DATE_DM: - begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end; - FORMAT_DATE_MY: - begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MY'; end; - FORMAT_TIME_MS: - begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MS'; end; - FORMAT_TIME_MSZ: - begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'MSZ'; end; - else - if (lXFData.FormatIndex > 0) and (lXFData.FormatIndex <= 58) then begin - ANumberFormat := fmts[lXFData.FormatIndex]; - ADecimals := decs[lXFData.FormatIndex]; + procedure FixMilliseconds; + var + isLong, isAMPM, isInterval: Boolean; + decs: Word; + i: Integer; + begin + if IsTimeFormat(ANumberFormatStr, isLong, isAMPM, isInterval, decs) + and (decs > 0) + then + for i:= Length(ANumberFormatStr) downto 1 do + case ANumberFormatStr[i] of + '0': ANumberFormatStr[i] := 'z'; + '.': break; end; - end; - end else - // Check custom formats if they have "/" in format string (this can fail for - // custom text formats) - if IsPercentNumberFormat(lFormatData.FormatString, ADecimals) then - ANumberFormat := nfPercentage - else - if IsExpNumberFormat(lFormatData.Formatstring, ADecimals) then - ANumberFormat := nfExp - else - if IsThousandSepNumberFormat(lFormatData.FormatString, ADecimals) then - ANumberFormat := nfFixedTh - else - if IsFixedNumberFormat(lFormatData.FormatString, ADecimals) then - ANumberFormat := nfFixed - else begin - t := IsTimeFormat(lFormatData.FormatString, isLongTime, isAMPM, isMilliSec); - d := IsDateFormat(lFormatData.FormatString); - if d and t then - ANumberFormat := nfShortDateTime - else - if d then - ANumberFormat := nfShortDate - else - if t then begin - if isAMPM then begin - if isLongTime then - ANumberFormat := nfLongTimeAM - else - ANumberFormat := nfShortTimeAM; - end else begin - if isLongTime then - ANumberFormat := nfLongTime - else - ANumberFormat := nfShortTime; - end; - end; + end; + +var + lNumFormatData: TsNumFormatData; +begin + lNumFormatData := FindNumFormatDataForCell(AXFIndex); + if lNumFormatData <> nil then begin + ANumberFormat := lNumFormatData.NumFormat; + ANumberFormatStr := lNumFormatData.FormatString; + ADecimals := lNumFormatData.Decimals; + FixMilliseconds; + end else begin + ANumberFormat := nfGeneral; + ANumberFormatStr := ''; + ADecimals := 0; end; end; { Determines the format data (for numerical formatting) which belong to a given - XF record. - Does not return data for built-in formats. } -function TsSpreadBIFFReader.FindFormatDataForCell(const AXFIndex: Integer - ): TFormatListData; + XF record. } +function TsSpreadBIFFReader.FindNumFormatDataForCell(const AXFIndex: Integer + ): TsNumFormatData; var lXFData: TXFListData; i: Integer; begin - lXFData := TXFListData(FXFList.Items[AXFIndex]); - for i := 0 to FFormatList.Count-1 do begin - Result := TFormatListData(FFormatList.Items[i]); - if Result.Index = lXFData.FormatIndex then Exit; - end; Result := nil; + lXFData := TXFListData(FXFList.Items[AXFIndex]); + i := NumFormatList.Find(lXFData.FormatIndex); + if i <> -1 then Result := NumFormatList[i]; end; { Convert the number to a date/time and return that if it is } @@ -772,7 +803,7 @@ function TsSpreadBIFFReader.IsDateTime(Number: Double; ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): boolean; begin if ANumberFormat in [ - nfShortDateTime, nfFmtDateTime, nfShortDate, + nfShortDateTime, nfFmtDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM] then begin ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode); @@ -788,7 +819,7 @@ begin end; end; -// In BIFF 8 it seams to always use the UTF-16 codepage +// In BIFF8 it seams to always use the UTF-16 codepage procedure TsSpreadBIFFReader.ReadCodePage(AStream: TStream); var lCodePage: Word; @@ -892,6 +923,76 @@ begin // to be overridden end; +{ Reads a FORMULA record, retrieves the RPN formula and puts the result in the + corresponding field. The formula is not recalculated here! + Valid for BIFF5 and BIFF8. } +procedure TsSpreadBIFFReader.ReadFormula(AStream: TStream); +var + ARow, ACol: Cardinal; + XF: WORD; + ResultFormula: Double; + Data: array [0..7] of BYTE; + Flags: WORD; + FormulaSize: BYTE; + i: Integer; + dt: TDateTime; + nf: TsNumberFormat; + nd: Word; + nfs: String; + +begin + { BIFF Record header } + { BIFF Record data } + { Index to XF Record } + ReadRowColXF(AStream, ARow, ACol, XF); + + { Result of the formula in IEE 754 floating-point value } + AStream.ReadBuffer(Data, Sizeof(Data)); + + { Options flags } + Flags := WordLEtoN(AStream.ReadWord); + + { Not used } + AStream.ReadDWord; + + { Formula size } + FormulaSize := WordLEtoN(AStream.ReadWord); + + { Formula data, output as debug info } +{ Write('Formula Element: '); + for i := 1 to FormulaSize do + Write(IntToHex(AStream.ReadByte, 2) + ' '); + WriteLn('');} + + //RPN data not used by now + AStream.Position := AStream.Position + FormulaSize; + + if (Data[6] = $FF) and (Data[7] = $FF) then + case Data[0] of + 0: FWorksheet.WriteUTF8Text(ARow, ACol, '(String)'); + 1: FWorksheet.WriteUTF8Text(ARow, ACol, '(Bool)'); + 2: FWorksheet.WriteUTF8Text(ARow, ACol, '(ERROR)'); + 3: FWorksheet.WriteUTF8Text(ARow, ACol, '(empty)'); + end + else begin + if SizeOf(Double) <> 8 then + raise Exception.Create('Double is not 8 bytes'); + + // Result is a number or a date/time + Move(Data[0], ResultFormula, SizeOf(Data)); + + {Find out what cell type, set content type and value} + ExtractNumberFormat(XF, nf, nd, nfs); + if IsDateTime(ResultFormula, nf, dt) then + FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs) + else + FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd); + end; + + {Add attributes} + ApplyCellFormatting(ARow, ACol, XF); +end; + // Reads multiple blank cell records // Valid for BIFF5 and BIFF8 (does not exist before) procedure TsSpreadBIFFReader.ReadMulBlank(AStream: TStream); @@ -1168,6 +1269,15 @@ begin // "15" is the index of the last pre-defined xf record end; +{ Creates the correct version of the number format list. It is for BIFF file + formats. + Valid for BIFF5.BIFF8. Needs to be overridden for BIFF2. } +procedure TsSpreadBIFFWriter.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsBIFFNumFormatList.Create; +end; + function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID( AElementKind: TFEKind; out ASecondaryID: Word): Word; const @@ -1469,6 +1579,30 @@ begin WriteNumber(AStream, ARow, ACol, ExcelDateSerial, ACell); end; +{ Writes a BIFF format record defined in AFormatData. AListIndex the index of + the formatdata in the format list (not the FormatIndex!). + Needs to be overridden by descendants. } +procedure TsSpreadBIFFWriter.WriteFormat(AStream: TStream; + AFormatData: TsNumFormatData; AListIndex: Integer); +begin + // needs to be overridden +end; + +{ Writes all number formats to the stream. Saving starts at the item with the + FirstFormatIndexInFile. } +procedure TsSpreadBIFFWriter.WriteFormats(AStream: TStream); +var + i: Integer; +begin + ListAllNumFormats; + i := NumFormatList.Find(NumFormatList.FirstFormatIndexInFile); + while i < NumFormatList.Count do begin + if NumFormatList[i] <> nil then + WriteFormat(AStream, NumFormatList[i], i); + inc(i); + end; +end; + { Writes a 64-bit floating point NUMBER record. Valid for BIFF5 and BIFF8 (BIFF2 has a different record structure.). } procedure TsSpreadBIFFWriter.WriteNumber(AStream: TStream; @@ -1806,204 +1940,5 @@ begin AStream.WriteWord(WordToLE(lXFIndex)); end; - -{ Format checking procedures } - -{ This simple parsing procedure of the Excel format string checks for a fixed - float format s, i.e. s can be '0', '0.00', '000', '0,000', and returns the - number of decimals, i.e. number of zeros behind the decimal point } -function IsFixedNumberFormat(s: String; out Decimals: Word): Boolean; -var - i: Integer; - p: Integer; - decs: String; -begin - Decimals := 0; - - // Check if s is a valid format mask. - try - FormatFloat(s, 1.0); - except - on EConvertError do begin - Result := false; - exit; - end; - end; - - // If it is count the zeros - each one is a decimal. - if s = '0' then - Result := true - else begin - p := pos('.', s); // position of decimal point; - if p = 0 then begin - Result := false; - end else begin - Result := true; - for i:= p+1 to Length(s) do - if s[i] = '0' then begin - inc(Decimals) - end - else - exit; // ignore characters after the last 0 - end; - end; -end; - -{ This function checks whether the format string corresponds to a thousand - separator format like "#,##0.000' and returns the number of fixed decimals - (i.e. zeros after the decimal point) } -function IsThousandSepNumberFormat(s: String; out Decimals: Word): Boolean; -var - i, p: Integer; -begin - Decimals := 0; - - // Check if s is a valid format string - try - FormatFloat(s, 1.0); - except - on EConvertError do begin - Result := false; - exit; - end; - end; - - // If it is look for the thousand separator. If found count decimals. - Result := (Pos(',', s) > 0); - if Result then begin - p := pos('.', s); - if p > 0 then - for i := p+1 to Length(s) do - if s[i] = '0' then - inc(Decimals) - else - exit; // ignore format characters after the last 0 - end; -end; - - -{ This function checks whether the format string corresponds to percent - formatting and determines the number of decimals } -function IsPercentNumberFormat(s: String; out Decimals: Word): Boolean; -var - i, p: Integer; -begin - Decimals := 0; - // The signature of the percent format is a percent sign at the end of the - // format string. - Result := (s <> '') and (s[Length(s)] = '%'); - if Result then begin - // Check for a valid format string - Delete(s, Length(s), 1); - try - FormatDateTime(s, 1.0); - except - on EConvertError do begin - Result := false; - exit; - end; - end; - // Count decimals - p := pos('.', s); - if p > 0 then - for i := p+1 to Length(s)-1 do - if s[i] = '0' then - inc(Decimals) - else - exit; // ignore characters after last 0 - end; -end; - -{ This function checks whether the format string corresponds to exponential - formatting and determines the number of decimals } -function IsExpNumberFormat(s: String; out Decimals: Word): Boolean; -var - i, p, pe: Integer; -begin - Result := false; - Decimals := 0; - - if SameText(s, 'General') then - exit; - - // Check for a valid format string - try - FormatDateTime(s, 1.0); - except - on EConvertError do begin - exit; - end; - end; - - // Count decimals - pe := pos('e', lowercase(s)); - result := pe > 0; - if Result then begin - p := pos('.', s); - if (p > 0) then begin - if p < pe then - for i:=1 to pe-1 do - if s[i] = '0' then - inc(Decimals) - else - exit; // ignore characters after last 0 - end; - end; -end; - -{ IsDateFormat checks if the format string s corresponds to a date format } -function IsDateFormat(s: String): Boolean; -begin - // Day, month, year are separated by a slash - Result := (pos('/', s) > 0); - if Result then - // Check validity of format string - try - FormatDateTime(s, now); - except on EConvertError do - Result := false; - end; -end; - -{ IsTimeFormat checks if the format string s is a time format. isLong is - true if the string contains hours, minutes and seconds (two colons). - isAMPM is true if the string contains "AM/PM", "A/P" or "AMPM". - isMilliSec is true if the string ends with a "z". } -function IsTimeFormat(s: String; out isLong, isAMPM, isMillisec: Boolean): Boolean; -var - p, i, count: Integer; -begin - // Time parts are separated by a colon - p := pos(':', s); - isLong := false; - isAMPM := false; - result := p > 0; - - if Result then begin - count := 1; - s := Uppercase(s); - - // If there are is a second colon s is a "long" time format - for i:=p+1 to Length(s) do - if s[i] = ':' then begin - isLong := true; - break; - end; - - // Seek for "AM/PM" etc to detect that specific format - isAMPM := (pos('AM/PM', s) > 0) or (pos('A/P', s) > 0) or (pos('AMPM', s) > 0); - - // Look for the "milliseconds" character z - isMilliSec := (s[Length(s)] = 'Z'); - - // Check validity of format string - try - FormatDateTime(s, now); - except on EConvertError do - Result := false; - end; - end; -end; - end.