From 283a28fabddae140e3e3a7f89d75c4ee73c3f09a Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 14 May 2014 23:17:46 +0000 Subject: [PATCH] fpspreadsheet: Fix crash of unit test. Still some number format detection issues. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3046 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel2demo/excel2read.lpr | 1 - .../examples/excel8demo/excel8write.lpr | 5 + .../examples/fpsgrid/fpsgrid.lpi | 123 +++++++------ components/fpspreadsheet/fpsopendocument.pas | 32 ++++ components/fpspreadsheet/fpspreadsheet.pas | 80 ++++++++- .../fpspreadsheet/tests/formattests.pas | 2 +- .../fpspreadsheet/tests/stringtests.pas | 2 +- components/fpspreadsheet/tests/testbiff8.xls | Bin 39936 -> 34304 bytes components/fpspreadsheet/xlsbiff2.pas | 165 +++--------------- components/fpspreadsheet/xlsbiff8.pas | 18 +- components/fpspreadsheet/xlscommon.pas | 86 ++++----- 11 files changed, 257 insertions(+), 257 deletions(-) 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 5093f294b127017cdd24bc7695d018bc6e6d49c9..a3c729c5e13de9e47c387725f20d396de300bb51 100644 GIT binary patch delta 3401 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 f8+_;!MgHsrekx8GH`5@n24 - *) + { 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.