From 3113794fab6ed30d2b4af7b5881f8d7e268fcbb9 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sat, 24 Jan 2015 22:19:55 +0000 Subject: [PATCH] fpspreadsheet: Fix crash when reading a BIFF5 file written by Excel97 (it has data (the directory) beyound the workbook stream which is not expected by the BIFF5/8 readers). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3897 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/fpsctrls/demo_ctrls.lpi | 2 +- .../examples/fpsctrls/demo_ctrls.lpr | 2 +- .../fpspreadsheet/examples/fpsctrls/main.lfm | 4 +- .../fpspreadsheet/examples/fpsctrls/main.pas | 38 +++++++++----- components/fpspreadsheet/fpspreadsheet.pas | 8 +-- components/fpspreadsheet/xlsbiff5.pas | 51 ++++++++++--------- components/fpspreadsheet/xlsbiff8.pas | 3 ++ 7 files changed, 65 insertions(+), 43 deletions(-) diff --git a/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpi b/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpi index 645e9223e..4eb378a3f 100644 --- a/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpi +++ b/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpi @@ -67,7 +67,7 @@ - + diff --git a/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpr b/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpr index cdf79767d..955483b58 100644 --- a/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpr +++ b/components/fpspreadsheet/examples/fpsctrls/demo_ctrls.lpr @@ -15,7 +15,7 @@ uses begin RequireDerivedFormResource := True; Application.Initialize; - Application.CreateForm(TForm1, Form1); + Application.CreateForm(TMainForm, MainForm); Application.Run; end. diff --git a/components/fpspreadsheet/examples/fpsctrls/main.lfm b/components/fpspreadsheet/examples/fpsctrls/main.lfm index 28b91138d..163eb10b2 100644 --- a/components/fpspreadsheet/examples/fpsctrls/main.lfm +++ b/components/fpspreadsheet/examples/fpsctrls/main.lfm @@ -1,9 +1,9 @@ -object Form1: TForm1 +object MainForm: TMainForm Left = 495 Height = 600 Top = 132 Width = 929 - Caption = 'Form1' + Caption = 'demo_ctrls' ClientHeight = 580 ClientWidth = 929 Menu = MainMenu diff --git a/components/fpspreadsheet/examples/fpsctrls/main.pas b/components/fpspreadsheet/examples/fpsctrls/main.pas index 1c5756984..a027b8b3b 100644 --- a/components/fpspreadsheet/examples/fpsctrls/main.pas +++ b/components/fpspreadsheet/examples/fpsctrls/main.pas @@ -11,9 +11,9 @@ uses type - { TForm1 } + { TMainForm } - TForm1 = class(TForm) + TMainForm = class(TForm) AcRowDelete: TAction; AcColDelete: TAction; AcRowAdd: TAction; @@ -286,21 +286,22 @@ type procedure InspectorTabControlChange(Sender: TObject); private { private declarations } + procedure UpdateCaption; public { public declarations } end; var - Form1: TForm1; + MainForm: TMainForm; implementation {$R *.lfm} -{ TForm1 } +{ TMainForm } { Loads the spreadsheet file selected by the AcFileOpen action } -procedure TForm1.AcFileOpenAccept(Sender: TObject); +procedure TMainForm.AcFileOpenAccept(Sender: TObject); begin WorkbookSource.AutodetectFormat := false; case AcFileOpen.Dialog.FilterIndex of @@ -314,10 +315,11 @@ begin 8: WorkbookSource.FileFormat := sfCSV; // Text files end; WorkbookSource.FileName := AcFileOpen.Dialog.FileName; // this loads the file + UpdateCaption; end; { Saves the spreadsheet to the file selected by the AcFileSaveAs action } -procedure TForm1.AcFileSaveAsAccept(Sender: TObject); +procedure TMainForm.AcFileSaveAsAccept(Sender: TObject); var fmt: TsSpreadsheetFormat; begin @@ -333,20 +335,21 @@ begin 7: fmt := sfWikiTable_WikiMedia; end; WorkbookSource.SaveToSpreadsheetFile(AcFileSaveAs.Dialog.FileName, fmt); + UpdateCaption; finally Screen.Cursor := crDefault; end; end; { Adds a column before the active cell } -procedure TForm1.AcColAddExecute(Sender: TObject); +procedure TMainForm.AcColAddExecute(Sender: TObject); begin WorksheetGrid.InsertCol(WorksheetGrid.Col); WorksheetGrid.Col := WorksheetGrid.Col + 1; end; { Deletes the column with the active cell } -procedure TForm1.AcColDeleteExecute(Sender: TObject); +procedure TMainForm.AcColDeleteExecute(Sender: TObject); var c: Integer; begin @@ -356,14 +359,14 @@ begin end; { Adds a row before the active cell } -procedure TForm1.AcRowAddExecute(Sender: TObject); +procedure TMainForm.AcRowAddExecute(Sender: TObject); begin WorksheetGrid.InsertRow(WorksheetGrid.Row); WorksheetGrid.Row := WorksheetGrid.Row + 1; end; { Deletes the row with the active cell } -procedure TForm1.AcRowDeleteExecute(Sender: TObject); +procedure TMainForm.AcRowDeleteExecute(Sender: TObject); var r: Integer; begin @@ -373,17 +376,28 @@ begin end; { Toggles the spreadsheet inspector on and off } -procedure TForm1.AcViewInspectorExecute(Sender: TObject); +procedure TMainForm.AcViewInspectorExecute(Sender: TObject); begin InspectorTabControl.Visible := AcViewInspector.Checked; end; { Event handler to synchronize the mode of the spreadsheet inspector with the selected tab of the TabControl } -procedure TForm1.InspectorTabControlChange(Sender: TObject); +procedure TMainForm.InspectorTabControlChange(Sender: TObject); begin Inspector.Mode := TsInspectorMode(InspectorTabControl.TabIndex); end; +procedure TMainForm.UpdateCaption; +begin + if WorkbookSource = nil then + Caption := 'demo_ctrls' + else + Caption := Format('demo_ctrls - "%s" [%s]', [ + WorkbookSource.Filename, + GetFileFormatName(WorkbookSource.Workbook.FileFormat) + ]); +end; + end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index a9659c4ae..7a16d9d52 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -6399,8 +6399,8 @@ class function TsWorkbook.GetFormatFromFileHeader(const AFileName: TFileName; const BIFF2_HEADER: array[0..15] of byte = ( $09,$00, $04,$00, $00,$00, $10,$00, $31,$00, $0A,$00, $C8,$00, $00,$00); - BIFF58_HEADER: array[0..15] of byte = ( - $D0,$CF, $11,$E0, $A1,$B1, $1A,$E1, $00,$00, $00,$00, $00,$00, $00,$00); + BIFF58_HEADER: array[0..7] of byte = ( + $D0,$CF, $11,$E0, $A1,$B1, $1A,$E1); BIFF5_MARKER: array[0..7] of widechar = ( 'B', 'o', 'o', 'k', #0, #0, #0, #0); BIFF8_MARKER:array[0..7] of widechar = ( @@ -6419,7 +6419,7 @@ begin // Read first 16 bytes stream.ReadBuffer(buf, 16); - // Check for Excel 2# + // Check for Excel 2 ok := true; for i:=0 to 15 do if buf[i] <> BIFF2_HEADER[i] then @@ -6434,7 +6434,7 @@ begin end; // Check for Excel 5 or 8 - for i:=0 to 15 do + for i:=0 to 7 do if buf[i] <> BIFF58_HEADER[i] then exit; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index f21bcc34e..91ad3dde2 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -387,30 +387,29 @@ begin CurStreamPos := AStream.Position; case RecordType of + INT_EXCEL_ID_BLANK : ReadBlank(AStream); + INT_EXCEL_ID_BOOLERROR : ReadBool(AStream); + INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); + INT_EXCEL_ID_NUMBER : ReadNumber(AStream); + INT_EXCEL_ID_LABEL : ReadLabel(AStream); + INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard. + INT_EXCEL_ID_RK : ReadRKValue(AStream); //(RK) This record represents a cell that contains an RK value (encoded integer or floating-point value). If a floating-point value cannot be encoded to an RK value, a NUMBER record will be written. This record replaces the record INTEGER written in BIFF2. + INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); + INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); + INT_EXCEL_ID_STANDARDWIDTH : ReadStandardWidth(AStream, FWorksheet); + INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream); + INT_EXCEL_ID_ROW : ReadRowInfo(AStream); + INT_EXCEL_ID_FORMULA : ReadFormula(AStream); + INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream); + INT_EXCEL_ID_STRING : ReadStringRecord(AStream); + INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); + INT_EXCEL_ID_PANE : ReadPane(AStream); + INT_EXCEL_ID_BOF : ; + INT_EXCEL_ID_EOF : SectionEOF := True; - INT_EXCEL_ID_BLANK : ReadBlank(AStream); - INT_EXCEL_ID_BOOLERROR : ReadBool(AStream); - INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream); - INT_EXCEL_ID_NUMBER : ReadNumber(AStream); - INT_EXCEL_ID_LABEL : ReadLabel(AStream); - INT_EXCEL_ID_RSTRING : ReadRichString(AStream); //(RSTRING) This record stores a formatted text cell (Rich-Text). In BIFF8 it is usually replaced by the LABELSST record. Excel still uses this record, if it copies formatted text cells to the clipboard. - INT_EXCEL_ID_RK : ReadRKValue(AStream); //(RK) This record represents a cell that contains an RK value (encoded integer or floating-point value). If a floating-point value cannot be encoded to an RK value, a NUMBER record will be written. This record replaces the record INTEGER written in BIFF2. - INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream); - INT_EXCEL_ID_COLINFO : ReadColInfo(AStream); - INT_EXCEL_ID_STANDARDWIDTH : ReadStandardWidth(AStream, FWorksheet); - INT_EXCEL_ID_DEFCOLWIDTH : ReadDefColWidth(AStream); - INT_EXCEL_ID_ROW : ReadRowInfo(AStream); - INT_EXCEL_ID_FORMULA : ReadFormula(AStream); - INT_EXCEL_ID_SHAREDFMLA : ReadSharedFormula(AStream); - INT_EXCEL_ID_STRING : ReadStringRecord(AStream); - INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream); - INT_EXCEL_ID_PANE : ReadPane(AStream); - INT_EXCEL_ID_BOF : ; - INT_EXCEL_ID_EOF : SectionEOF := True; - -{$IFDEF FPSPREADDEBUG} // Only write out if debugging + {$IFDEF FPSPREADDEBUG} // Only write out if debugging else - // Show unsupported record types to console. + // Show unsupported record types to console. case RecordType of $000C: ; //(CALCCOUNT) This record is part of the Calculation Settings Block. It specifies the maximum number of times the formulas should be iteratively calculated. This is a fail-safe against mutually recursive formulas locking up a spreadsheet application. $000D: ; //(CALCMODE) This record is part of the Calculation Settings Block. It specifies whether to calculate formulas manually, automatically or automatically except for multiple table operations. @@ -445,7 +444,7 @@ begin else WriteLn(format('Record type: %.4X Record Size: %.4X',[RecordType,RecordSize])); end; -{$ENDIF} + {$ENDIF} end; // Make sure we are in the right position for the next record @@ -720,6 +719,7 @@ end; procedure TsSpreadBIFF5Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook); var BIFF5EOF: Boolean; + p,s: Int64; begin { Initializations } @@ -742,10 +742,15 @@ begin ReadWorksheet(AStream, AData); // Check for the end of the file + p := AStream.Position; + s := AStream.Size; if AStream.Position >= AStream.Size then BIFF5EOF := True; // Final preparations Inc(FCurrentWorksheet); + if FCurrentWorksheet = FWorksheetNames.Count then BIFF5EOF := True; + // It can happen in files written by Office97 that the OLE directory is + // at the end of the file. end; if not FPaletteFound then diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 59b3064b5..be30152c2 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -638,6 +638,9 @@ begin // Final preparations Inc(FCurrentWorksheet); + if FCurrentWorksheet = FWorksheetNames.Count then BIFF8EOF := True; + // It can happen in files written by Office97 that the OLE directory is + // at the end of the file. end; if not FPaletteFound then