diff --git a/components/fpspreadsheet/examples/excel2demo/excel2read.lpr b/components/fpspreadsheet/examples/excel2demo/excel2read.lpr index 2bad7981b..fd56de91d 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2read.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2read.lpr @@ -42,7 +42,6 @@ begin WriteLn('Row: ', CurCell^.Row, ' Col: ', CurCell^.Col, ' Value: ', UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col)) ); - WriteLn(MyWorkbook.GetFont(CurCell^.FontIndex).Size-11); CurCell := MyWorkSheet.GetNextCell(); end; diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index 526847711..f51763f6b 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -75,6 +75,7 @@ begin MyWorksheet.WriteBorderLineStyle(5, 5, cbNorth, lsThick); // F7, top border only, but different color + MyWorksheet.WriteBorders(6, 5, [cbNorth]); MyWorksheet.WriteBorderColor(6, 5, cbNorth, scGreen); MyWorksheet.WriteUTF8Text(6, 5, 'top border green or red?'); // Excel shows it to be red --> the upper border wins @@ -200,6 +201,10 @@ begin inc(r); MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz'); MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz'); + // NOTE: The upper option "MSZ" = "mm:ss.z" should result only in 1 decimal. + // This is true for writing, but in reading always 3 decimals are displayed. + // This is due to fpc's SysUtile.FormatDateTime which does not distinguish + // both cases. // Write formatted numbers number := 12345.67890123456789; diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index 642efb5fc..3d7ebb480 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -149,8 +149,8 @@ - - + + @@ -220,15 +220,18 @@ + + - - + + + - + @@ -263,7 +266,7 @@ - + @@ -298,7 +301,7 @@ - + @@ -308,11 +311,10 @@ - - + - + @@ -577,47 +579,47 @@ - + - + - - + + - - + + - + - + - + - + - + - + - + @@ -625,75 +627,75 @@ - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -723,6 +725,15 @@ + + + + + + + + + diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 4abd8dc51..3059d6020 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -46,6 +46,14 @@ type dm1904 {e.g. Quattro Pro,Mac Excel compatibility} ); + { TsSpreadOpenDocNumFormatList } + TsSpreadOpenDocNumFormatList = class(TsCustomNumFormatList) + protected + procedure AddBuiltinFormats; override; + public +// function FormatStringForWriting(AIndex: Integer): String; override; + end; + { TsSpreadOpenDocReader } TsSpreadOpenDocReader = class(TsCustomSpreadReader) @@ -58,6 +66,7 @@ type // Figures out what the base year for times in this file (dates are unambiguous) procedure ReadDateMode(SpreadSheetNode: TDOMNode); protected + procedure CreateNumFormatList; override; { Record writing methods } procedure ReadFormula(ARow : Word; ACol : Word; ACellNode: TDOMNode); procedure ReadLabel(ARow : Word; ACol : Word; ACellNode: TDOMNode); @@ -79,6 +88,8 @@ type // Streams with the contents of files FSMeta, FSSettings, FSStyles, FSContent, FSMimetype: TStringStream; FSMetaInfManifest: TStringStream; + // Helpers + procedure CreateNumFormatList; override; // Routines to write those files procedure WriteMimetype; procedure WriteMetaInfManifest; @@ -159,8 +170,23 @@ const DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime +{ TsSpreadOpenDocNumFormatList } + +procedure TsSpreadOpenDocNumFormatList.AddBuiltinFormats; +begin + // to be filled later... +end; + { TsSpreadOpenDocReader } +{ Creates the correct version of the number format list. + It is for ods file formats. } +procedure TsSpreadOpenDocReader.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsSpreadOpenDocNumFormatList.Create; +end; + function TsSpreadOpenDocReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; var i : integer; @@ -442,6 +468,12 @@ end; { TsSpreadOpenDocWriter } +procedure TsSpreadOpenDocWriter.CreateNumFormatList; +begin + FreeAndNil(FNumFormatList); + FNumFormatList := TsSpreadOpenDocNumFormatList.Create; +end; + procedure TsSpreadOpenDocWriter.WriteMimetype; begin FMimetype := 'application/vnd.oasis.opendocument.spreadsheet'; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 5684e9eb7..463497639 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -125,7 +125,18 @@ type {@@ Describes the type of content of a cell on a TsWorksheet } TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber, - cctUTF8String, cctDateTime); + cctUTF8String, cctDateTime, cctBool, cctError); + + {@@ Error code values } + TErrorValue = ( + errEmptyIntersection, // #NULL! + errDivideByZero, // #DIV/0! + errWrongType, // #VALUE! + errIllegalRef, // #REF! + errWrongName, // #NAME? + errOverflow, // #NUM! + errArgNotAvail // #N/A + ); {@@ List of possible formatting fields } TsUsedFormattingField = (uffTextRotation, uffFont, uffBold, uffBorder, @@ -278,6 +289,8 @@ type NumberValue: double; UTF8StringValue: ansistring; DateTimeValue: TDateTime; + BoolValue: Boolean; + StatusValue: Byte; { Formatting fields } UsedFormattingFields: TsUsedFormattingFields; FontIndex: Integer; @@ -369,8 +382,10 @@ type procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; AFormatString: String); overload; procedure WriteBlank(ARow, ACol: Cardinal); + procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean); procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); + procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); { Writing of cell attributes } @@ -688,6 +703,15 @@ resourcestring lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file'; lpUnknownSpreadsheetFormat = 'unknown format'; lpInvalidFontIndex = 'Invalid font index'; + lpTRUE = 'TRUE'; + lpFALSE = 'FALSE'; + lpErrEmptyIntersection = '#NULL!'; + lpErrDivideByZero = '#DIV/0!'; + lpErrWrongType = '#VALUE!'; + lpErrIllegalRef = '#REF!'; + lpErrWrongName = '#NAME?'; + lpErrOverflow = '#NUM!'; + lpErrArgNotAvail = '#N/A'; var {@@ @@ -1173,6 +1197,18 @@ begin Result := UTF8StringValue; cctDateTime: Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr, NumberDecimals); + cctBool: + Result := IfThen(BoolValue, lpTRUE, lpFALSE); + cctError: + case TErrorValue(StatusValue and $0F) of + errEmptyIntersection: Result := lpErrEmptyIntersection; + errDivideByZero : Result := lpErrDivideByZero; + errWrongType : Result := lpErrWrongType; + errIllegalRef : Result := lpErrIllegalRef; + errWrongName : Result := lpErrWrongName; + errOverflow : Result := lpErrOverflow; + errArgNotAvail : Result := lpErrArgNotAvail; + end; else Result := ''; end; @@ -1382,6 +1418,23 @@ begin ChangedCell(ARow, ACol); end; +{@@ + Writes as boolean cell + + @param ARow The row of the cell + @param ACol The column of the cell + @param AValue The boolean value +} +procedure TsWorksheet.WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean); +var + ACell: PCell; +begin + ACell := GetCell(ARow, ACol); + ACell^.ContentType := cctBool; + ACell^.BoolValue := AValue; + ChangedCell(ARow, ACol); +end; + {@@ Writes a date/time value to a determined cell @@ -1446,6 +1499,23 @@ begin ChangedCell(ARow, ACol); end; +{@@ + Writes a cell with an error. + + @param ARow The row of the cell + @param ACol The column of the cell + @param AValue The error code value +} +procedure TsWorksheet.WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue); +var + ACell: PCell; +begin + ACell := GetCell(ARow, ACol); + ACell^.ContentType := cctError; + ACell^.StatusValue := (ACell^.StatusValue and $F0) or ord(AValue); + ChangedCell(ARow, ACol); +end; + {@@ Writes a formula to a determined cell @@ -2553,8 +2623,12 @@ begin ANumFormat := nfShortDateTime else if isDate then ANumFormat := SHORT_LONG_DATE[isLongDate] - else if isTime then - ANumFormat := AMPM_SHORT_LONG_TIME[isAMPM, isLongTime] + else if isTime then begin + if (ADecimals > 0) and (not isAMPM) then + ANumFormat := nfFmtDateTime + else + ANumFormat := AMPM_SHORT_LONG_TIME[isAMPM, isLongTime] + end else if AFormatString <> '' then ANumFormat := nfCustom; end; diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index c0406372d..7150d29f1 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -189,7 +189,7 @@ begin SollDateTimeStrings[i, 6] := FormatDateTime('dd/mmm', SollDateTimes[i]); SollDateTimeStrings[i, 7] := FormatDateTime('mmm/yy', SollDateTimes[i]); SollDateTimeStrings[i, 8] := FormatDateTime('nn:ss', SollDateTimes[i]); - SollDateTimeStrings[i, 9] := TimeIntervalToString(SollDateTimes[i]); + SollDateTimeStrings[i, 9] := FormatDateTime('[h]:mm:ss', SollDateTimes[i]); end; // Column width diff --git a/components/fpspreadsheet/tests/stringtests.pas b/components/fpspreadsheet/tests/stringtests.pas index da6b7fe89..b13d2c51c 100644 --- a/components/fpspreadsheet/tests/stringtests.pas +++ b/components/fpspreadsheet/tests/stringtests.pas @@ -25,7 +25,7 @@ uses // Not using lazarus package as the user may be working with multiple versions // Instead, add .. to unit search path Classes, SysUtils, fpcunit, testutils, testregistry, - fpsallformats, fpspreadsheet, xlsbiff8 {and a project requirement for lclbase for utf8 handling}, + fpsallformats, fpsutils, fpspreadsheet, xlsbiff8 {and a project requirement for lclbase for utf8 handling}, testsutility; var diff --git a/components/fpspreadsheet/tests/testbiff8.xls b/components/fpspreadsheet/tests/testbiff8.xls index 5093f294b..a3c729c5e 100644 Binary files a/components/fpspreadsheet/tests/testbiff8.xls and b/components/fpspreadsheet/tests/testbiff8.xls differ diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index a51c199eb..19d4c325f 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -130,37 +130,6 @@ var $00FFFF // $07: cyan ); -(* -{ 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 - available in fpspreadsheet are mapped to these 21 formats such that least - destruction is made. } - NUMFORMAT_BIFF2: array[0..20] of string = ( - 'General', // 0 - '0', - '0.00', - '#,##0', - '#,##0.00', - '"$"#,##0_);("$"#,##0)', // 5 - '"$"#,##0_);[Red]("$"#,##0)', - '"$"#,##0.00_);("$"#,##0.00)', - '"$"#,##0.00_);[Red]("$"#,##0.00)', - '0%', - '0.00%', // 10 - '0.00E+00', - 'M/D/YY', - 'D-MMM-YY', - 'D-MMM', - 'MMM-YY', // 15 - 'h:mm AM/PM', - 'h:mm:ss AM/PM', - 'h:mm', - 'h:mm:ss', - 'M/D/YY h:mm' // 20 - ); -*) implementation @@ -193,36 +162,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 - FORMAT_GENERAL = 0; //general/default format - FORMAT_FIXED_0_DECIMALS = 1; //fixed, 0 decimals - FORMAT_FIXED_2_DECIMALS = 2; //fixed, 2 decimals - FORMAT_FIXED_THOUSANDS_0_DECIMALS = 3; //fixed, w/ thousand separator, 0 decs - FORMAT_FIXED_THOUSANDS_2_DECIMALS = 4; //fixed, w/ thousand separator, 2 decs - FORMAT_CURRENCY_0_DECIMALS = 5; //currency (with currency symbol), 0 decs - FORMAT_CURRENCY_2_DECIMALS = 7; //currency (with currency symbol), 2 decs - FORMAT_PERCENT_0_DECIMALS = 9; //percent, 0 decimals - FORMAT_PERCENT_2_DECIMALS = 10; //percent, 2 decimals - FORMAT_EXP_2_DECIMALS = 11; //exponent, 2 decimals - FORMAT_SCI_1_DECIMAL = 11; //scientific, 1 decimal -- not present in BIFF2 -- mapped to EXP_2_DECIMALS - FORMAT_SHORT_DATE = 12; //short date - FORMAT_DATE_DM = 14; //date D-MMM - FORMAT_DATE_MY = 15; //date MMM-YYYY - FORMAT_SHORT_TIME_AM = 16; //short time H:MM with AM - FORMAT_LONG_TIME_AM = 17; //long time H:MM:SS with AM - FORMAT_SHORT_TIME = 18; //short time H:MM - FORMAT_LONG_TIME = 19; //long time H:MM:SS - FORMAT_SHORT_DATETIME = 20; //short date+time - { The next three formats are not available in BIFF2. They should not be used - when a file is to be saved in BIFF2. If it IS saved as BIFF2 the formats - are mapped to FORMAT_LONG_TIME} - 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 } @@ -351,74 +291,19 @@ end; procedure TsSpreadBIFF2Reader.ExtractNumberFormat(AXFIndex: WORD; out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormatStr: String); -const - NOT_USED = nfGeneral; - fmts: array[1..20] of TsNumberFormat = ( - nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5 - nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10 - nfExp, nfShortDate, nfShortDate, nfFmtDateTime, nfFmtDateTime, // 11..15 - nfShortTimeAM, nfLongTimeAM, nfShortTime, nfLongTime, nfShortDateTime// 16..20 - ); - decs: array[1..20] 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 - ); var lNumFormatData: TsNumFormatData; - lXFData: TXFListData; - isAMPM: Boolean; - isLongTime: Boolean; - isMilliSec: Boolean; - t,d: Boolean; begin - ANumberFormat := nfGeneral; - ANumberFormatStr := ''; - ADecimals := 0; - (* 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 - ANumberFormat := fmts[lXFData.FormatIndex]; - ADecimals := decs[lXFData.FormatIndex]; - end; - end else - // The next is copied from xlscommon - I think it's not necessary here - if IsPercentNumberFormat(lNumFormatData.FormatString, ADecimals) then - ANumberFormat := nfPercentage - else - if IsExpNumberFormat(lNumFormatData.Formatstring, ADecimals) then - ANumberFormat := nfExp - else - if IsThousandSepNumberFormat(lNumFormatData.FormatString, ADecimals) then - ANumberFormat := nfFixedTh - else - if IsFixedNumberFormat(lNumFormatData.FormatString, ADecimals) then - ANumberFormat := nfFixed - else begin - t := IsTimeFormat(lNumFormatData.FormatString, isLongTime, isAMPM, isMilliSec); - d := IsDateFormat(lNumFormatData.FormatString, isLongDate); - 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; *) + if lNumFormatData <> nil then begin + ANumberFormat := lNumFormatData.NumFormat; + ANumberFormatStr := lNumFormatData.FormatString; + ADecimals := lNumFormatData.Decimals; + end else begin + ANumberFormat := nfGeneral; + ANumberFormatStr := ''; + ADecimals := 0; + end; end; procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream); @@ -513,22 +398,20 @@ begin CurStreamPos := AStream.Position; case RecordType of - - INT_EXCEL_ID_BLANK : ReadBlank(AStream); - INT_EXCEL_ID_FONT : ReadFont(AStream); - INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); - INT_EXCEL_ID_INTEGER : ReadInteger(AStream); - INT_EXCEL_ID_NUMBER : ReadNumber(AStream); - INT_EXCEL_ID_LABEL : ReadLabel(AStream); - INT_EXCEL_ID_FORMULA : ReadFormula(AStream); - INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream); - INT_EXCEL_ID_ROW : ReadRowInfo(AStream); - INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); - INT_EXCEL_ID_PANE : ReadPane(AStream); - INT_EXCEL_ID_XF : ReadXF(AStream); - INT_EXCEL_ID_BOF : ; - INT_EXCEL_ID_EOF : BIFF2EOF := True; - + INT_EXCEL_ID_BLANK : ReadBlank(AStream); + INT_EXCEL_ID_FONT : ReadFont(AStream); + INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream); + INT_EXCEL_ID_INTEGER : ReadInteger(AStream); + INT_EXCEL_ID_NUMBER : ReadNumber(AStream); + INT_EXCEL_ID_LABEL : ReadLabel(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); + INT_EXCEL_ID_COLWIDTH : ReadColWidth(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); + INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); + INT_EXCEL_ID_PANE : ReadPane(AStream); + INT_EXCEL_ID_XF : ReadXF(AStream); + INT_EXCEL_ID_BOF : ; + INT_EXCEL_ID_EOF : BIFF2EOF := True; else // nothing end; diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 727cd06ba..9c51dc89c 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -78,21 +78,16 @@ type procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); procedure ReadBoundsheet(AStream: TStream); function ReadString(const AStream: TStream; const ALength: WORD): UTF8String; - procedure ReadSST(const AStream: TStream); - procedure ReadLabelSST(const AStream: TStream); - // Read XF record - procedure ReadXF(const AStream: TStream); - // Workbook Globals records - // procedure ReadCodepage in xlscommon - // procedure ReadDateMode in xlscommon + protected + procedure ReadBlank(AStream: TStream); override; procedure ReadFont(const AStream: TStream); procedure ReadFormat(AStream: TStream); override; - { Record reading methods } - procedure ReadBlank(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override; + procedure ReadLabelSST(const AStream: TStream); procedure ReadRichString(const AStream: TStream); - protected + procedure ReadSST(const AStream: TStream); procedure ReadStringRecord(AStream: TStream; var AStringResult: String); override; + procedure ReadXF(const AStream: TStream); public destructor Destroy; override; { General reading methods } @@ -1750,16 +1745,13 @@ procedure TsSpreadBIFF8Reader.ReadStringRecord(AStream: TStream; var record_id: Word; record_size: word; - p: Cardinal; begin record_id := WordLEToN(AStream.ReadWord); if record_id <> INT_EXCEL_ID_STRING then raise Exception.Create('ReadStringRecord: wrong record found.'); record_size := WordLEToN(AStream.ReadWord); - p := AStream.Position; AStringResult := ReadWideString(AStream, false); - AStream.Position := p + record_size; end; procedure TsSpreadBIFF8Reader.ReadXF(const AStream: TStream); diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 871072dab..df8c00827 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -237,33 +237,6 @@ 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 - FORMAT_GENERAL = 0; //general/default format - FORMAT_FIXED_0_DECIMALS = 1; //fixed, 0 decimals - FORMAT_FIXED_2_DECIMALS = 2; //fixed, 2 decimals - FORMAT_FIXED_THOUSANDS_0_DECIMALS = 3; //fixed, w/ thousand separator, 0 decs - FORMAT_FIXED_THOUSANDS_2_DECIMALS = 4; //fixed, w/ thousand separator, 2 decs - FORMAT_CURRENCY_0_DECIMALS = 5; //currency (with currency symbol), 0 decs - FORMAT_CURRENCY_2_DECIMALS = 7; //currency (with currency symbol), 2 decs - FORMAT_PERCENT_0_DECIMALS = 9; //percent, 0 decimals - FORMAT_PERCENT_2_DECIMALS = 10; //percent, 2 decimals - FORMAT_EXP_2_DECIMALS = 11; //exponent, 2 decimals - FORMAT_SHORT_DATE = 14; //short date - FORMAT_DATE_DM = 16; //date D-MMM - FORMAT_DATE_MY = 17; //date MMM-YYYY - FORMAT_SHORT_TIME_AM = 18; //short time H:MM with AM - FORMAT_LONG_TIME_AM = 19; //long time H:MM:SS with AM - FORMAT_SHORT_TIME = 20; //short time H:MM - FORMAT_LONG_TIME = 21; //long time H:MM:SS - FORMAT_SHORT_DATETIME = 22; //short date+time - FORMAT_TIME_MS = 45; //time MM:SS - 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; @@ -336,6 +309,14 @@ const MASK_XF_VERT_ALIGN_BOTTOM = $20; MASK_XF_VERT_ALIGN_JUSTIFIED = $30; + { Error codes } + ERR_INTERSECTION_EMPTY = $00; // #NULL! + ERR_DIVIDE_BY_ZERO = $07; // #DIV/0! + ERR_WRONG_TYPE_OF_OPERAND = $0F; // #VALUE! + ERR_ILLEGAL_REFERENCE = $17; // #REF! + ERR_WRONG_NAME = $1D; // #NAME? + ERR_OVERFLOW = $24; // #NUM! + ERR_NOT_AVAILABLE = $2A; // #N/A type TDateMode=(dm1900,dm1904); //DATEMODE values, 5.28 @@ -943,6 +924,7 @@ var nd: Word; nfs: String; resultStr: String; + err: TErrorValue; begin { BIFF Record header } @@ -970,18 +952,33 @@ begin //RPN data not used by now AStream.Position := AStream.Position + FormulaSize; - + (* + // Now determine the type of the formula result if (Data[6] = $FF) and (Data[7] = $FF) then case Data[0] of 0: begin ReadStringRecord(AStream, resultStr); - FWorksheet.WriteUTF8Text(ARow, ACol, resultStr); - end; - 1: FWorksheet.WriteUTF8Text(ARow, ACol, '(Bool)'); - 2: FWorksheet.WriteUTF8Text(ARow, ACol, '(ERROR)'); - 3: FWorksheet.WriteUTF8Text(ARow, ACol, '(empty)'); + if resultStr = '' then + FWorksheet.WriteBlank(ARow, ACol) + else + FWorksheet.WriteUTF8Text(ARow, ACol, resultStr); + end; + 1: FWorksheet.WriteBoolValue(ARow, ACol, Data[2] = 1); + 2: begin + case Data[2] of + ERR_INTERSECTION_EMPTY : err := errEmptyIntersection; + ERR_DIVIDE_BY_ZERO : err := errDivideByZero; + ERR_WRONG_TYPE_OF_OPERAND: err := errWrongType; + ERR_ILLEGAL_REFERENCE : err := errIllegalRef; + ERR_WRONG_NAME : err := errWrongName; + ERR_OVERFLOW : err := errOverflow; + ERR_NOT_AVAILABLE : err := errArgNotAvail; + end; + FWorksheet.WriteErrorValue(ARow, ACol, err); + end; + 3: FWorksheet.WriteBlank(ARow, ACol); end - else begin + else begin *) if SizeOf(Double) <> 8 then raise Exception.Create('Double is not 8 bytes'); @@ -994,7 +991,7 @@ begin FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs) else FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd); - end; +// end; {Add attributes} ApplyCellFormatting(ARow, ACol, XF); @@ -1076,7 +1073,7 @@ var nd: word; nfs: String; begin - ReadRowColXF(AStream,ARow,ACol,XF); + ReadRowColXF(AStream, ARow, ACol, XF); { IEE 754 floating-point value } AStream.ReadBuffer(value, 8); @@ -1609,14 +1606,21 @@ end; procedure TsSpreadBIFFWriter.WriteFormats(AStream: TStream); var i: Integer; + + item: TsNumFormatData; + begin ListAllNumFormats; + + item := NumFormatList[20]; + i := NumFormatList.Find(NumFormatList.FirstFormatIndexInFile); - while i < NumFormatList.Count do begin - if NumFormatList[i] <> nil then - WriteFormat(AStream, NumFormatList[i], i); - inc(i); - end; + if i > -1 then + 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.