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
This commit is contained in:
wp_xxyyzz
2015-01-24 22:19:55 +00:00
parent 9d89f1dcf3
commit 3113794fab
7 changed files with 65 additions and 43 deletions

View File

@ -67,7 +67,7 @@
<Unit1>
<Filename Value="main.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="Form1"/>
<ComponentName Value="MainForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="main"/>

View File

@ -15,7 +15,7 @@ uses
begin
RequireDerivedFormResource := True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -387,28 +387,27 @@ 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.
case RecordType of
@ -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

View File

@ -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