From f638eb928a33255acf4c0c89e4a7f5139d81ecf9 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sat, 17 May 2014 15:13:08 +0000 Subject: [PATCH] fpspreadsheet: Write currency format codes for BIFF5 and BIFF8 (incl. "red" for negatives and "-" for zero). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3053 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel2demo/excel2write.lpr | 23 +++ .../examples/excel5demo/excel5write.lpr | 22 +++ .../examples/excel8demo/excel8write.lpr | 27 ++- .../examples/fpsgrid/fpsgrid.lpi | 156 ++++++++--------- .../examples/fpsgrid/mainform.pas | 2 +- components/fpspreadsheet/fpspreadsheet.pas | 160 +++++++++++------- .../fpspreadsheet/fpspreadsheetgrid.pas | 4 + components/fpspreadsheet/fpsutils.pas | 102 +++++++++-- components/fpspreadsheet/xlsbiff2.pas | 25 +-- components/fpspreadsheet/xlscommon.pas | 94 +++++++--- 10 files changed, 416 insertions(+), 199 deletions(-) diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr index c5a22ba7e..4f9ee124e 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr @@ -267,7 +267,29 @@ begin MyWorksheet.WriteFontColor(r, 3, scGray); MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3); MyWorksheet.WriteFontColor(r, 4, scGray); + + inc(r,2); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, '$'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrency, 0, '$'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrency, 0, '$'); inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDash, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDash, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDashRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDashRed, 0, 'USD'); + + inc(r, 2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); MyWorksheet.WriteNumber(r, 1, number); MyWorksheet.WriteFontColor(r, 1, scGray); @@ -344,6 +366,7 @@ begin MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h'); MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h'); MyWorksheet.WriteFontColor(r, 1, scGray); + inc(r); // Set width of columns 0 to 3 MyWorksheet.WriteColWidth(0, 50); diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr index 991bdbf42..9a5e73b47 100644 --- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr +++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr @@ -273,7 +273,29 @@ begin 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,2); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrency, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrency, 0, 'USD'); inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDash, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDash, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDashRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDashRed, 0, 'USD'); + + inc(r, 2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); MyWorksheet.WriteNumber(r, 1, number); MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0_);("$"#,##0)'); diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index f51763f6b..0a4c3de62 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -37,6 +37,8 @@ begin MyWorkbook := TsWorkbook.Create; MyWorkbook.SetDefaultFont('Calibri', 9); MyWorkbook.UsePalette(@PALETTE_BIFF8, Length(PALETTE_BIFF8)); + MyWorkbook.FormatSettings.CurrencyFormat := 2; + MyWorkbook.FormatSettings.NegCurrFormat := 14; MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet1); MyWorksheet.Options := MyWorksheet.Options - [soShowGridLines]; @@ -247,7 +249,8 @@ begin MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs'); MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); - inc(r); + + inc(r,2); MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); @@ -283,7 +286,29 @@ begin 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,2); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrency, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrency, 0, 'USD'); inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDash, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDash, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDash, 0, 'USD'); + inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyDashRed, 0 decs'); + MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDashRed, 0, 'USD'); + MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDashRed, 0, 'USD'); + + inc(r,2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "EUR "#,##0_);("EUR "#,##0)'); MyWorksheet.WriteDateTime(r, 1, number); MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"EUR "#,##0_);("EUR "#,##0)'); diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index 481f20d0e..f04d7864a 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -128,8 +128,8 @@ - - + + @@ -140,20 +140,20 @@ - + - - + + - + - - + + @@ -231,11 +231,11 @@ - + - - - + + + @@ -264,10 +264,10 @@ - + - - + + @@ -289,32 +289,26 @@ - - - + + - - - - - @@ -590,124 +584,124 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - + @@ -739,7 +733,7 @@ - + diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.pas b/components/fpspreadsheet/examples/fpsgrid/mainform.pas index 6d5cfb426..4ebb8c337 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.pas +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.pas @@ -410,7 +410,7 @@ begin exit; cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col)); if (cell <> nil) then begin - decs := cell^.NumberDecimals; + decs := cell^.Decimals; if (Sender = AcIncDecimals) then Worksheet.WriteDecimals(cell, decs+1); if (Sender = AcDecDecimals) and (decs > 0) then diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index b4fe47b71..b8965e385 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -153,9 +153,18 @@ type {@@ Number/cell formatting. Only uses a subset of the default formats, 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, + TsNumberFormat = ( + // general-purpose for all numbers + nfGeneral, + // numbers + nfFixed, nfFixedTh, nfExp, nfSci, nfPercentage, + // currency + nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed, + // dates and times nfShortDateTime, nfFmtDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime, - nfShortTimeAM, nfLongTimeAM, nfTimeInterval, nfCustom); + nfShortTimeAM, nfLongTimeAM, nfTimeInterval, + // other (format string goes directly into the file) + nfCustom); {@@ Text rotation formatting. The text is rotated relative to the standard orientation, which is from left to right horizontal: @@ -306,7 +315,8 @@ type BackgroundColor: TsColor; NumberFormat: TsNumberFormat; NumberFormatStr: String; - NumberDecimals: Word; + Decimals: Byte; + CurrencySymbol: String; RGBBackgroundColor: TFPColor; // only valid if BackgroundColor=scRGBCOLOR end; @@ -383,7 +393,8 @@ type { Writing of values } procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; - AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); overload; + AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2; + ACurrencySymbol: String = ''); overload; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; AFormatString: String); overload; procedure WriteBlank(ARow, ACol: Cardinal); @@ -459,6 +470,7 @@ type { Internal methods } procedure RemoveWorksheetsCallback(data, arg: pointer); public + FormatSettings: TFormatSettings; { Base methods } constructor Create; destructor Destroy; override; @@ -514,7 +526,8 @@ type public Index: Integer; NumFormat: TsNumberFormat; - Decimals: Integer; + Decimals: Byte; + CurrencySymbol: String; FormatString: string; end; @@ -529,20 +542,22 @@ type FNextFormatIndex: Integer; procedure AddBuiltinFormats; virtual; procedure Analyze(AFormatIndex: Integer; var AFormatString: String; - var ANumFormat: TsNumberFormat; var ADecimals: Word); virtual; + var ANumFormat: TsNumberFormat; var ADecimals: byte; + var ACurrencySymbol: String); 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; + AFormatString: String = ''; ADecimals: Byte = 0; + ACurrencySymbol: String = ''): 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; + ADecimals: Byte; ACurrencySymbol: String): Integer; overload; function Find(AFormatIndex: Integer): Integer; overload; function FormatStringForWriting(AIndex: Integer): String; virtual; procedure Sort; @@ -976,7 +991,8 @@ begin cell^.TextRotation := AFormat^.TextRotation; cell^.NumberFormat := AFormat^.NumberFormat; cell^.NumberFormatStr := AFormat^.NumberFormatStr; - cell^.NumberDecimals := AFormat^.NumberDecimals; + cell^.Decimals := AFormat^.Decimals; + cell^.CurrencySymbol := AFormat^.CurrencySymbol; ChangedCell(AToRow, AToCol); ChangedFont(AToRow, AToCol); @@ -1190,8 +1206,11 @@ end; function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; function FloatToStrNoNaN(const Value: Double; - ANumberFormat: TsNumberFormat; ANumberFormatStr: string; ADecimals: Word): ansistring; + ANumberFormat: TsNumberFormat; ANumberFormatStr: string; ADecimals: byte): ansistring; + var + fs: TFormatSettings; begin + fs := FWorkbook.FormatSettings; if IsNan(Value) then Result := '' else @@ -1199,12 +1218,12 @@ function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; Result := SciFloat(Value, ADecimals) else if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then - Result := FloatToStr(Value) + Result := FloatToStr(Value, fs) else if (ANumberFormat = nfPercentage) then - Result := FormatFloat(ANumberFormatStr, Value*100) // '%' is already added to FormatStr. + Result := FormatFloat(ANumberFormatStr, Value*100, fs) else - Result := FormatFloat(ANumberFormatStr, Value); + Result := FormatFloat(ANumberFormatStr, Value, fs) end; function DateTimeToStrNoNaN(const Value: Double; @@ -1212,15 +1231,10 @@ function TsWorksheet.ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; begin Result := ''; if not IsNaN(Value) then begin - (* - if ANumberFormat = nfTimeInterval then - Result := TimeIntervalToString(Value, ANumberFormatStr) - else - *) if ANumberFormatStr = '' then Result := FormatDateTime('c', Value) else - Result := FormatDateTime(ANumberFormatStr, Value); + Result := FormatDateTimeEx(ANumberFormatStr, Value); end; end; @@ -1238,11 +1252,11 @@ begin with ACell^ do case ContentType of cctNumber: - Result := FloatToStrNoNaN(NumberValue, NumberFormat, NumberFormatStr, NumberDecimals); + Result := FloatToStrNoNaN(NumberValue, NumberFormat, NumberFormatStr, Decimals); cctUTF8String: Result := UTF8StringValue; cctDateTime: - Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr, NumberDecimals); + Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr, Decimals); cctBool: Result := IfThen(BoolValue, lpTRUE, lpFALSE); cctError: @@ -1265,33 +1279,15 @@ var ACell: PCell; Str: string; begin + Result := 0.0; ACell := FindCell(ARow, ACol); - if ACell = nil then - begin - Result := 0.0; - Exit; - end; + exit; case ACell^.ContentType of - - //cctFormula - cctDateTime : Result := ACell^.DateTimeValue; //this is in FPC TDateTime format, not Excel - cctNumber : Result := ACell^.NumberValue; - cctUTF8String: - begin - // The try is necessary to catch errors while converting the string - // to a number, an operation which may fail - try - Str := ACell^.UTF8StringValue; - Result := StrToFloat(Str); - except - Result := 0.0; - end; - end; - - else - Result := 0.0; + cctDateTime : Result := ACell^.DateTimeValue; //this is in FPC TDateTime format, not Excel + cctNumber : Result := ACell^.NumberValue; + cctUTF8String : TryStrToFloat(ACell^.UTF8StringValue, Result); end; end; @@ -1395,21 +1391,27 @@ end; @param ANumber The number to be written @param AFormat The format identifier, e.g. nfFixed (optional) @param ADecimals The number of decimals used for formatting (optional) + @param ACurrencySymbol The currency symbol in case of currency format (nfCurrency) } procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: double; - AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); + AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2; + ACurrencySymbol: String = ''); var ACell: PCell; + fs: TFormatSettings; begin ACell := GetCell(ARow, ACol); ACell^.ContentType := cctNumber; ACell^.NumberValue := ANumber; - ACell^.NumberDecimals := ADecimals; + ACell^.Decimals := ADecimals; if AFormat <> nfGeneral then begin Include(ACell^.UsedFormattingFields, uffNumberFormat); ACell^.NumberFormat := AFormat; - WriteDecimals(ACell, ADecimals); + ACell^.Decimals := ADecimals; + ACell^.CurrencySymbol := ACurrencySymbol; + ACell^.NumberFormatStr := BuildNumFormatString(ACell^.NumberFormat, + Workbook.FormatSettings, ADecimals, ACurrencySymbol); end; ChangedCell(ARow, ACol); end; @@ -1539,8 +1541,9 @@ end; procedure TsWorksheet.WriteDecimals(ACell: PCell; ADecimals: Byte); begin if (ACell <> nil) and (ACell^.ContentType = cctNumber) then begin - ACell^.NumberDecimals := ADecimals; - ACell^.NumberFormatStr := BuildNumFormatString(ACell^.NumberFormat, ADecimals); + ACell^.Decimals := ADecimals; + ACell^.NumberFormatStr := BuildNumFormatString(ACell^.NumberFormat, + FWorkbook.FormatSettings, ADecimals, ACell^.CurrencySymbol); ChangedCell(ACell^.Row, ACell^.Col); end; end; @@ -1598,7 +1601,8 @@ begin Include(ACell^.UsedFormattingFields, uffNumberFormat); ACell^.NumberFormat := ANumberFormat; if (AFormatString = '') then - ACell^.NumberFormatStr := BuildNumFormatString(ANumberFormat, ACell^.NumberDecimals) + ACell^.NumberFormatStr := BuildNumFormatString(ANumberFormat, + Workbook.FormatSettings, ACell^.Decimals, ACell^.CurrencySymbol) else ACell^.NumberFormatStr := AFormatString; ChangedCell(ARow, ACol); @@ -1966,6 +1970,7 @@ constructor TsWorkbook.Create; begin inherited Create; FWorksheets := TFPList.Create; + FormatSettings := DefaultFormatSettings; FFontList := TFPList.Create; SetDefaultFont('Arial', 10.0); InitFonts; @@ -2585,7 +2590,7 @@ 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; + AFormatString: String = ''; ADecimals: byte = 0; ACurrencySymbol: String = ''): integer; var item: TsNumFormatData; begin @@ -2594,6 +2599,7 @@ begin item.NumFormat := ANumFormat; item.FormatString := AFormatString; item.Decimals := ADecimals; + item.CurrencySymbol := ACurrencySymbol; Result := inherited Add(item); end; @@ -2607,8 +2613,12 @@ begin 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); + Result := AddFormat(FNextFormatIndex, + AFormatCell^.NumberFormat, + AFormatCell^.NumberFormatStr, + AFormatCell^.Decimals, + AFormatCell^.CurrencySymbol + ); inc(FNextFormatIndex); end; @@ -2634,7 +2644,7 @@ end; about the structure of the string in the actual file format. } procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer; var AFormatString: String; var ANumFormat: TsNumberFormat; - var ADecimals: Word); + var ADecimals: Byte; var ACurrencySymbol: String); const SHORT_LONG_DATE: array[boolean] of TsNumberFormat = ( nfShortDate, nfLongDate @@ -2646,6 +2656,10 @@ const EXP_SCI: array[boolean] of TsNumberFormat = ( nfExp, nfSci ); + CURR_FMT: array[boolean, boolean] of TsNumberFormat = ( + (nfCurrency, nfCurrencyDash), + (nfCurrencyRed, nfCurrencyDashRed) + ); var decs: Word; isAMPM: Boolean; @@ -2654,6 +2668,7 @@ var isInterval: Boolean; isSci: Boolean; isTime, isDate: Boolean; + isCurrRed, isCurrDash: Boolean; lFormatData: TsNumFormatData; i: Integer; fmt: String; @@ -2685,6 +2700,13 @@ begin IsTimeFormat(AFormatString, isLongTime, isAMPM, isInterval, ADecimals); exit; end; + nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed: + begin + ANumFormat := lFormatData.NumFormat; + AFormatString := lFormatData.FormatString; + ADecimals := lFormatData.Decimals; + ACurrencySymbol := lFormatData.CurrencySymbol; + end; end; end; @@ -2702,6 +2724,9 @@ begin else if IsFixedNumberFormat(AFormatString, ADecimals) then ANumFormat := nfFixed + else + if IsCurrencyFormat(AFormatString, ADecimals, ACurrencySymbol, isCurrRed, isCurrDash) then + ANumFormat := CURR_FMT[isCurrRed, isCurrDash] else begin isTime := IsTimeFormat(AFormatString, isLongTime, isAMPM, isInterval, ADecimals); isDate := IsDateFormat(AFormatString, isLongDate); @@ -2731,16 +2756,17 @@ procedure TsCustomNumFormatList.AnalyzeAndAdd(AFormatIndex: Integer; AFormatString: String); var nf: TsNumberFormat; - decs: Word; + decs: Byte; + currsym: String; begin if Find(AFormatIndex) > -1 then - raise Exception.Create('TsCustomNumFormatList.AnalyzeAndAdd: Format index must be unique.'); + exit; // Analyze the format string and extract information for internal formatting - Analyze(AFormatIndex, AFormatString, nf, decs); + Analyze(AFormatIndex, AFormatString, nf, decs, currsym); // Add the new item - AddFormat(AFormatIndex, nf, AFormatString, decs); + AddFormat(AFormatIndex, nf, AFormatString, decs, currSym); end; { Clears the list and frees memory occupied by the format items. } @@ -2767,13 +2793,14 @@ begin if AFormatCell = nil then Result := -1 else - Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr, AFormatCell^.NumberDecimals); + Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr, + AFormatCell^.Decimals, AFormatCell^.CurrencySymbol); 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; + AFormatString: String; ADecimals: Byte; ACurrencySymbol: String): Integer; var item: TsNumFormatData; fmt: String; @@ -2837,6 +2864,8 @@ begin and (item.NumFormat = ANumFormat) and (item.FormatString = AFormatString) and (item.Decimals = ADecimals) + and (not (item.NumFormat in [nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed]) + or (item.CurrencySymbol = ACurrencySymbol)) then exit; end; @@ -3037,7 +3066,12 @@ begin if (FFormattingStyles[i].NumberFormat <> AFormat^.NumberFormat) then Continue; case AFormat^.NumberFormat of nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci: - if (FFormattingStyles[i].NumberDecimals <> AFormat^.NumberDecimals) then Continue; + if (FFormattingStyles[i].Decimals <> AFormat^.Decimals) then Continue; + nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed: + begin + if (FFormattingStyles[i].Decimals <> AFormat^.Decimals) then Continue; + if (FFormattingStyles[i].CurrencySymbol <> AFormat^.CurrencySymbol) then Continue; + end; nfShortDate, nfLongDate, nfShortDateTime, nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfFmtDateTime, nfTimeInterval, nfCustom: if (FFormattingstyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue; @@ -3058,11 +3092,11 @@ end; procedure TsCustomSpreadWriter.FixFormat(ACell: PCell); var isLong, isAMPM, isInterval: Boolean; - decs: Word; + decs: Byte; begin if ACell^.NumberFormat = nfFmtDateTime then begin if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then - ACell^.NumberDecimals := decs; + ACell^.Decimals := decs; end; end; diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 1056637f5..2edb14d9e 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -665,6 +665,10 @@ begin Canvas.Font.Size := round(fnt.Size); end; end; + if (lCell^.NumberFormat in [nfCurrencyRed, nfCurrencyDashRed]) and + not IsNaN(lCell^.NumberValue) and (lCell^.NumberValue < 0) + then + Canvas.Font.Color := FWorkbook.GetPaletteColor(scRed); // Wordwrap, text alignment and text rotation are handled by "DrawTextInCell". end; end; diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index fa1ede4ac..81685a55b 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -60,21 +60,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 IsCurrencyFormat(s: String; out Decimals: Byte; out CurrSymbol: String; + out IsCurrencyRedFmt, IsCurrencyDashFmt: Boolean): Boolean; +function IsExpNumberFormat(s: String; out Decimals: Byte; out IsSci: Boolean): Boolean; +function IsFixedNumberFormat(s: String; out Decimals: Byte): Boolean; +function IsPercentNumberFormat(s: String; out Decimals: Byte): Boolean; +function IsThousandSepNumberFormat(s: String; out Decimals: Byte): Boolean; function IsDateFormat(s: String; out IsLong: Boolean): Boolean; function IsTimeFormat(s: String; out isLong, isAMPM, isInterval: Boolean; - out SecDecimals: Word): Boolean; + out SecDecimals: Byte): Boolean; -function BuildNumFormatString(ANumberFormat: TsNumberFormat; ADecimals: Byte): String; +function BuildNumFormatString(ANumberFormat: TsNumberFormat; + const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; + ACurrencySymbol: String = '?'): String; -function SciFloat(AValue: Double; ADecimals: Word): String; +function SciFloat(AValue: Double; ADecimals: Byte): String; //function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String; procedure MakeTimeIntervalMask(Src: String; var Dest: String); -function FormatDateTime(const FormatStr: string; DateTime: TDateTime): string; +function FormatDateTimeEx(const FormatStr: string; DateTime: TDateTime): string; implementation @@ -493,7 +497,7 @@ end; { 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; +function IsFixedNumberFormat(s: String; out Decimals: Byte): Boolean; var i: Integer; p: Integer; @@ -540,7 +544,7 @@ 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; +function IsThousandSepNumberFormat(s: String; out Decimals: Byte): Boolean; var i, p: Integer; begin @@ -571,7 +575,7 @@ 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; +function IsPercentNumberFormat(s: String; out Decimals: Byte): Boolean; var i, p: Integer; begin @@ -600,11 +604,18 @@ begin end; end; +{ This function checks whether the format string corresponds to a currency format. } +function IsCurrencyFormat(s: String; out Decimals: Byte; out CurrSymbol: String; + out IsCurrencyRedFmt, IsCurrencyDashFmt: Boolean): Boolean; +begin + Result := false; // TO DO !!!! +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; +function IsExpNumberFormat(s: String; out Decimals: Byte; out IsSci: Boolean): Boolean; var i, pdp, pe, ph: Integer; @@ -674,7 +685,7 @@ end; 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; + out SecDecimals: Byte): Boolean; var p, pdp, i, count: Integer; begin @@ -734,10 +745,41 @@ end; { Builds a number format string from the numberformat code and the count of decimals. } function BuildNumFormatString(ANumberFormat: TsNumberFormat; - ADecimals: Byte): String; + const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; + ACurrencySymbol: String = '?'): String; +const + POS_FMT: array[0..3] of string = ( //0: value, 1: currency symbol + '"%1:s"%0:s', + '%0:s"%1:s"', + '"%1:s" %0:s', + '%0:s "%1:s"' + ); + NEG_FMT: array[0..15] of string = ( + '("%1:s"%0:s)', // 0 + '-"%1:s"%0:s', // 1 + '"%1:s"-%0:s', // 2 + '"%1:s"%0:s-', // 3 + '(%0:s"%1:s")', // 4 + '-%0:s"%1:s"', // 5 + '-%0:s-"%1:s"', // 6 + '%0:s"%1:s"-', // 7 + '-%0:s "%1:s"', // 8 + '-"%1:s" %0:s', // 9 + '%0:s "%1:s"-', // 10 + '"%1:s" %0:s-', // 11 + '"%1:s" -%0:s', // 12 + '%0:s- "%1:s"', // 13 + '("%1:s" %0:s)', // 14 + '(%0:s "%1:s")' // 15 + ); var decs: String; + cf, ncf: Byte; begin + cf := AFormatSettings.CurrencyFormat; + ncf := AFormatSettings.NegCurrFormat; + if ADecimals = -1 then ADecimals := AFormatSettings.CurrencyDecimals; + if ACurrencySymbol = '?' then ACurrencySymbol := AFormatSettings.CurrencyString; decs := DupeString('0', ADecimals); if ADecimals > 0 then decs := '.' + decs; case ANumberFormat of @@ -751,15 +793,39 @@ begin Result := '##0' + decs + 'E+0'; nfPercentage: Result := '0' + decs + '%'; - else - Result := ''; + nfCurrency, + nfCurrencyRed, + nfCurrencyDash, + nfCurrencyDashRed: + begin + Result := ''; + if ACurrencySymbol <> '' then + Result := Format(POS_FMT[cf], ['#,##0' + decs, ACurrencySymbol]) + ';' + + Format(NEG_FMT[ncf], ['#,##0' + decs, ACurrencySymbol]) + else begin + Result := '#,##0' + decs; + case ncf of + 0, 14, 15 : Result := Result + ';(#,##0' + decs + ')'; + 1, 5, 6, 8, 9, 12: Result := Result + ';-#,##0' + decs; + else Result := Result + ';#,##0' + decs + '-'; + end; + end; + if ANumberFormat in [nfCurrency, nfCurrencyRed] then begin + Result := Result + ';0' + decs; + if cf in [2,3] then + Result := Format('%s "%s"', [Result, ACurrencySymbol]) + else + Result := Format('%s"%s"', [Result, ACurrencySymbol]); + end else + Result := Result + ';-'; + 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; +function SciFloat(AValue: Double; ADecimals: Byte): String; var m: Double; ex: Integer; @@ -1177,7 +1243,7 @@ begin result := StrPas(@ResultBuffer[0]); end ; -function FormatDateTime(const FormatStr: string; DateTime: TDateTime): string; +function FormatDateTimeEx(const FormatStr: string; DateTime: TDateTime): string; begin DateTimeToString(Result, FormatStr, DateTime, DefaultFormatSettings); end; diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index c486e8002..34cea77b0 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -58,8 +58,8 @@ type 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; + out ANumberFormat: TsNumberFormat; out ADecimals: Byte; + out ACurrencySymbol: String; out ANumberFormatStr: String); override; procedure ReadBlank(AStream: TStream); override; procedure ReadColWidth(AStream: TStream); procedure ReadFont(AStream: TStream); @@ -289,8 +289,8 @@ 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; - out ANumberFormat: TsNumberFormat; out ADecimals: Word; - out ANumberFormatStr: String); + out ANumberFormat: TsNumberFormat; out ADecimals: Byte; + out ACurrencySymbol: String; out ANumberFormatStr: String); var lNumFormatData: TsNumFormatData; begin @@ -299,10 +299,12 @@ begin ANumberFormat := lNumFormatData.NumFormat; ANumberFormatStr := lNumFormatData.FormatString; ADecimals := lNumFormatData.Decimals; + ACurrencySymbol := lNumFormatData.CurrencySymbol; end else begin ANumberFormat := nfGeneral; ANumberFormatStr := ''; ADecimals := 0; + ACurrencySymbol := ''; end; end; @@ -470,7 +472,8 @@ var value: Double; dt: TDateTime; nf: TsNumberFormat; - nd: Word; + nd: Byte; + ncs: String; nfs: String; begin { BIFF Record row/column/style } @@ -480,11 +483,11 @@ begin AStream.ReadBuffer(value, 8); {Find out what cell type, set content type and value} - ExtractNumberFormat(XF, nf, nd, nfs); + ExtractNumberFormat(XF, nf, nd, ncs, nfs); if IsDateTime(value, nf, dt) then FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs) else - FWorksheet.WriteNumber(ARow, ACol, value, nf, nd); + FWorksheet.WriteNumber(ARow, ACol, value, nf, nd, ncs); { Apply formatting to cell } ApplyCellFormatting(ARow, ACol, XF); @@ -723,15 +726,17 @@ var begin case ACell.NumberFormat of nfExp: - if ACell.NumberDecimals <> 2 then begin - ACell.NumberDecimals := 2; + if ACell.Decimals <> 2 then begin + ACell.Decimals := 2; + ACell.CurrencySymbol := ''; ACell.NumberFormatStr := '0.00E+00'; end; nfSci: begin ACell.NumberFormat := nfExp; ACell.NumberFormatStr := '0.00E+00'; - ACell.NumberDecimals := 2; + ACell.Decimals := 2; + ACell.CurrencySymbol := ''; end; nfFmtDateTime: begin diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index e50f7a9a9..b1f7d4bc9 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -349,7 +349,8 @@ type protected procedure AddBuiltinFormats; override; procedure Analyze(AFormatIndex: Integer; var AFormatString: String; - var ANumFormat: TsNumberFormat; var ADecimals: Word); override; + var ANumFormat: TsNumberFormat; var ADecimals: Byte; + var ACurrencySymbol: String); override; public function FormatStringForWriting(AIndex: Integer): String; override; end; @@ -369,8 +370,8 @@ type function DecodeRKValue(const ARK: DWORD): Double; // Returns the numberformat for a given XF record procedure ExtractNumberFormat(AXFIndex: WORD; - out ANumberFormat: TsNumberFormat; out ADecimals: Word; - out ANumberFormatStr: String); virtual; + out ANumberFormat: TsNumberFormat; out ADecimals: Byte; + out ACurrencySymbol: String; out ANumberFormatStr: String); virtual; // Finds format record for XF record pointed to by cell // Will not return info for built-in formats function FindNumFormatDataForCell(const AXFIndex: Integer): TsNumFormatData; @@ -532,19 +533,41 @@ begin end; +{ TsBIFFNumFormatParser } (* + +constructor TsBIFFNumFormatParser.Create(AFormatString: String); +begin + inherited; + FFormatString := AFormatString; + Parse; +end; + +procedure TsBIFFNumFormatParser.Parse; +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; +var + cs: String; begin + cs := DefaultFormatSettings.CurrencyString; + 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( 5, nfCurrency, '', 0); + AddFormat( 6, nfCurrencyRed, '', 0); // negative numbers in red + AddFormat( 7, nfCurrency, '', 2); + AddFormat( 8, nfCurrencyRed, '', 2); AddFormat( 9, nfPercentage, '0%', 0); AddFormat(10, nfPercentage, '0.00%', 2); AddFormat(11, nfExp, '0.00E+00', 2); @@ -558,10 +581,18 @@ begin AddFormat(20, nfShortTime); AddFormat(21, nfLongTime); AddFormat(22, nfShortDateTime); - // 23..44 not supported + // 23..36 not supported + AddFormat(37, nfCurrency, '', 0); + AddFormat(38, nfCurrencyRed, '', 0); + AddFormat(39, nfCurrency, '', 2); + AddFormat(40, nfCurrencyRed, '', 2); + AddFormat(41, nfCurrencyDash, '', 0); + AddFormat(42, nfCurrencyDashRed, '', 0); + AddFormat(43, nfCurrencyDash, '', 2); + AddFormat(44, nfCurrencyDashRed, '', 2); AddFormat(45, nfFmtDateTime, 'nn:ss'); AddFormat(46, nfTimeInterval, '[h]:nn:ss'); - AddFormat(47, nfFmtDateTime, 'nn:ss.z'); // z will be replace by 0 later + AddFormat(47, nfFmtDateTime, 'nn:ss.z'); // z will be replaced by 0 later AddFormat(48, nfSci, '##0.0E+00', 1); // 49 ("Text") not supported @@ -575,7 +606,7 @@ end; The output values will be passed to fpc. } procedure TsBIFFNumFormatList.Analyze(AFormatIndex: Integer; var AFormatString: String; var ANumFormat: TsNumberFormat; - var ADecimals: Word); + var ADecimals: Byte; var ACurrencySymbol: String); var fmt: String; begin @@ -612,12 +643,14 @@ begin ANumFormat := nfShortTime; AFormatString := ''; end; + ADecimals := 0; + exit; end; - ADecimals := 0; - exit; + + // TO DO: Analyze currency end; - inherited Analyze(AFormatIndex, AFormatString, ANumFormat, ADecimals); + inherited Analyze(AFormatIndex, AFormatString, ANumFormat, ADecimals, ACurrencySymbol); end; { Creates formatting strings that are written into the file. } @@ -644,6 +677,11 @@ begin // if added by user. // We check here for safety and add the brackets if not there. MakeTimeIntervalMask(item.FormatString, Result); + nfCurrencyRed, nfCurrencyDashRed: + begin + i := Pos(';', item.FormatString); + Result := Copy(item.FormatString, 1, i) + '[RED]'+ Copy(item.FormatString, i+1, Length(item.FormatString)); + end; end; end; @@ -763,13 +801,13 @@ end; { Extracts number format data from an XF record index by AXFIndex. Valid for BIFF5-BIFF8. Needs to be overridden for BIFF2 } procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD; - out ANumberFormat: TsNumberFormat; out ADecimals: Word; - out ANumberFormatStr: String); + out ANumberFormat: TsNumberFormat; out ADecimals: Byte; + out ACurrencySymbol: String; out ANumberFormatStr: String); procedure FixMilliseconds; var isLong, isAMPM, isInterval: Boolean; - decs: Word; + decs: Byte; i: Integer; begin if IsTimeFormat(ANumberFormatStr, isLong, isAMPM, isInterval, decs) @@ -790,11 +828,13 @@ begin ANumberFormat := lNumFormatData.NumFormat; ANumberFormatStr := lNumFormatData.FormatString; ADecimals := lNumFormatData.Decimals; + ACurrencySymbol := lNumFormatData.CurrencySymbol; FixMilliseconds; end else begin ANumberFormat := nfGeneral; ANumberFormatStr := ''; ADecimals := 0; + ACurrencySymbol := ''; end; end; @@ -951,7 +991,8 @@ var i: Integer; dt: TDateTime; nf: TsNumberFormat; - nd: Word; + nd: Byte; + ncs: String; nfs: String; resultStr: String; err: TErrorValue; @@ -1014,11 +1055,11 @@ begin Move(Data[0], ResultFormula, SizeOf(Data)); {Find out what cell type, set content type and value} - ExtractNumberFormat(XF, nf, nd, nfs); + ExtractNumberFormat(XF, nf, nd, ncs, nfs); if IsDateTime(ResultFormula, nf, dt) then FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs) else - FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd); + FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd, ncs); end; {Add attributes} @@ -1061,8 +1102,9 @@ var pending: integer; RK: DWORD; nf: TsNumberFormat; - nd: word; + nd: Byte; nfs: String; + ncs: String; begin ARow := WordLEtoN(AStream.ReadWord); fc := WordLEtoN(AStream.ReadWord); @@ -1072,11 +1114,11 @@ begin RK := DWordLEtoN(AStream.ReadDWord); lNumber := DecodeRKValue(RK); {Find out what cell type, set contenttype and value} - ExtractNumberFormat(XF, nf, nd, nfs); + ExtractNumberFormat(XF, nf, nd, ncs, nfs); if IsDateTime(lNumber, nf, lDateTime) then FWorksheet.WriteDateTime(ARow, fc, lDateTime, nf, nfs) else - FWorksheet.WriteNumber(ARow, fc, lNumber, nf, nd); + FWorksheet.WriteNumber(ARow, fc, lNumber, nf, nd, ncs); inc(fc); dec(pending, SizeOf(XF) + SizeOf(RK)); end; @@ -1098,8 +1140,9 @@ var value: Double; dt: TDateTime; nf: TsNumberFormat; - nd: word; + nd: Byte; nfs: String; + ncs: String; begin ReadRowColXF(AStream, ARow, ACol, XF); @@ -1107,11 +1150,11 @@ begin AStream.ReadBuffer(value, 8); {Find out what cell type, set content type and value} - ExtractNumberFormat(XF, nf, nd, nfs); + ExtractNumberFormat(XF, nf, nd, ncs, nfs); if IsDateTime(value, nf, dt) then FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs) else - FWorksheet.WriteNumber(ARow, ACol, value, nf, nd); + FWorksheet.WriteNumber(ARow, ACol, value, nf, nd, ncs); { Add attributes to cell } ApplyCellFormatting(ARow, ACol, XF); @@ -1178,7 +1221,8 @@ var lDateTime: TDateTime; Number: Double; nf: TsNumberFormat; // Number format - nd: Word; // decimals + nd: Byte; // decimals + ncs: String; // Currency symbol nfs: String; // Number format string begin {Retrieve XF record, row and column} @@ -1191,11 +1235,11 @@ begin Number := DecodeRKValue(RK); {Find out what cell type, set contenttype and value} - ExtractNumberFormat(XF, nf, nd, nfs); + ExtractNumberFormat(XF, nf, nd, ncs, nfs); if IsDateTime(Number, nf, lDateTime) then FWorksheet.WriteDateTime(ARow, ACol, lDateTime, nf, nfs) else - FWorksheet.WriteNumber(ARow, ACol, Number, nf); + FWorksheet.WriteNumber(ARow, ACol, Number, nf, nd, ncs); {Add attributes} ApplyCellFormatting(ARow, ACol, XF);