unit mainform; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, ExtCtrls, iniFiles, fpstypes, fpSpreadsheet; type { TForm1 } TForm1 = class(TForm) Bevel1: TBevel; BtnWrite: TButton; BtnRead: TButton; CgFormats: TCheckGroup; CgRowCount: TCheckGroup; CbVirtualModeOnly: TCheckBox; LblCancel: TLabel; Panel1: TPanel; Memo: TMemo; ParameterPanel: TPanel; RgContent: TRadioGroup; StatusBar: TStatusBar; procedure BtnReadClick(Sender: TObject); procedure BtnWriteClick(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); procedure FormCreate(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: char); private { private declarations } FDir: String; FEscape: Boolean; FCurFormat: TsSpreadsheetFormat; procedure EnableControls(AEnable: Boolean); function GetRowCount(AIndex: Integer): Integer; procedure ReadCellDataHandler(Sender: TObject; ARow, ACol: Cardinal; const ADataCell: PCell); procedure WriteCellStringHandler(Sender: TObject; ARow, ACol: Cardinal; var AValue: Variant; var AStyleCell: PCell); procedure WriteCellNumberHandler(Sender: TObject; ARow, ACol: Cardinal; var AValue: Variant; var AStyleCell: PCell); procedure WriteCellStringAndNumberHandler(Sender: TObject; ARow, ACol: Cardinal; var AValue: Variant; var AStyleCell: PCell); procedure ReadFromIni; procedure WriteToIni; procedure RunReadTest(Idx: Integer; Log: String; Options: TsWorkbookOptions); procedure RunWriteTest(Idx: integer; Rows: integer; Log: string; Options: TsWorkbookOptions); procedure StatusMsg(const AMsg: String); public { public declarations } end; var Form1: TForm1; implementation uses LclIntf, StrUtils, fpsUtils; {$R *.lfm} const fmtODS = 0; fmtXLSX = 1; fmtXLS8 = 2; fmtXLS5 = 3; fmtXLS2 = 4; rc10k = 0; rc20k = 1; rc30k = 2; rc40k = 3; rc50k = 4; rc60k = 5; rc100k = 6; CONTENT_PREFIX: array[0..2] of Char = ('S', 'N', 'M'); CONTENT_TEXT: array[0..2] of string = ('strings only', 'numbers only', '50% strings and 50% numbers'); FORMAT_EXT: array[0..4] of String = ('.ods', '.xlsx', '.xls', '_b5.xls', '_b2.xls'); SPREAD_FORMAT: array[0..4] of TsSpreadsheetFormat = (sfOpenDocument, sfOOXML, sfExcel8, sfExcel5, sfExcel2); COLCOUNT = 100; { TForm1 } procedure TForm1.ReadCellDataHandler(Sender: TObject; ARow, ACol: Cardinal; const ADataCell: PCell); begin Unused(ACol, ADataCell); // nothing to do here. Just do a progress display if ARow mod 1000 = 0 then StatusMsg(Format('Virtual mode reading %s: Row %d...', [GetFileFormatName(FCurFormat), ARow])); end; procedure TForm1.WriteCellStringHandler(Sender: TObject; ARow, ACol: cardinal; var AValue: variant; var AStyleCell: PCell); var S: string; begin Unused(AStyleCell); S := 'Xy' + IntToStr(ARow) + 'x' + IntToStr(ACol); AValue := S; if ARow mod 1000 = 0 then StatusMsg(Format('Virtual mode writing %s: Row %d...', [GetFileFormatName(FCurFormat), ARow])); end; procedure TForm1.WriteCellNumberHandler(Sender: TObject; ARow, ACol: cardinal; var AValue: variant; var AStyleCell: PCell); begin UnUsed(AStyleCell); AValue := ARow * 1E5 + ACol; if ARow mod 1000 = 0 then StatusMsg(Format('Virtual mode writing %s: Row %d...', [GetFileFormatName(FCurFormat), ARow])); end; procedure TForm1.WriteCellStringAndNumberHandler(Sender: TObject; ARow, ACol: cardinal; var AValue: variant; var AStyleCell: PCell); begin if odd(ARow + ACol) then WriteCellStringHandler(Sender, ARow, ACol, AValue, AStyleCell) else WriteCellNumberHandler(Sender, ARow, ACol, AValue, AStyleCell); end; procedure TForm1.RunReadTest(Idx: Integer; Log: String; Options: TsWorkbookOptions); var MyWorkbook: TsWorkbook; Tm: DWord; fName, s: String; i, j: Integer; F: File; ok: Boolean; begin Unused(idx); s := Trim(Log); Log := Log + ' '; try if FEscape then begin Log := 'Test aborted'; exit; end; for i := 0 to CgFormats.Items.Count-1 do begin if FEscape then begin Log := 'Test aborted'; exit; end; if not CgFormats.Checked[i] then continue; FCurFormat := SPREAD_FORMAT[i]; StatusMsg('Reading ' + GetFileFormatName(FCurFormat)); ok := false; for j:=1 to 4 do begin if FEscape then begin Log := 'Test aborted'; exit; end; fName := FDir + CONTENT_PREFIX[RgContent.ItemIndex] + Copy(s, 1, Pos(' ', s)-1) + '_' + IntToStr(j) + FORMAT_EXT[i]; if not FileExists(fname) then continue; AssignFile(F, fname); Reset(F); if FileSize(F) = 0 then continue; CloseFile(F); MyWorkbook := TsWorkbook.Create; try Application.ProcessMessages; MyWorkbook.Options := Options; if boVirtualMode in Options then MyWorkbook.OnReadCellData := @ReadCellDataHandler; Tm := GetTickCount; try MyWorkbook.ReadFromFile(fname, SPREAD_FORMAT[i]); Log := Log + format('%5.1f ', [(GetTickCount - Tm) / 1000]); ok := true; break; except end; finally MyWorkbook.Free; end; end; if not ok then Log := Log + ' xxxx '; end; finally Memo.Append(TrimRight(Log)); StatusMsg(''); end; end; procedure TForm1.RunWriteTest(Idx: integer; Rows: integer; Log: string; Options: TsWorkbookOptions); var MyWorkbook: TsWorkbook; MyWorksheet: TsWorksheet; ARow, ACol: cardinal; Tm: DWORD; fName, S: string; k: Integer; begin MyWorkbook := TsWorkbook.Create; try if FEscape then begin Log := 'Test aborted'; exit; end; MyWorksheet := MyWorkbook.AddWorksheet('Sheet1'); MyWorkbook.Options := Options; Application.ProcessMessages; Tm := GetTickCount; try if boVirtualMode in Options then begin MyWorkbook.VirtualRowCount := Rows; MyWorkbook.VirtualColCount := COLCOUNT; case RgContent.ItemIndex of 0: MyWorkbook.OnWriteCellData := @WriteCellStringHandler; 1: MyWorkbook.OnWriteCellData := @WriteCellNumberHandler; 2: MyWorkbook.OnWriteCellData := @WriteCellStringAndNumberHandler; end; end else begin for ARow := 0 to Rows - 1 do begin if ARow mod 1000 = 0 then begin StatusMsg(Format('Building row %d...', [ARow])); if FEscape then begin Log := 'Test aborted'; exit; end; end; case RgContent.ItemIndex of 0: for ACol := 0 to COLCOUNT-1 do begin S := 'Xy' + IntToStr(ARow) + 'x' + IntToStr(ACol); MyWorksheet.WriteUTF8Text(ARow, ACol, S); end; 1: for ACol := 0 to COLCOUNT-1 do MyWorksheet.WriteNumber(ARow, ACol, 1E5*ARow + ACol); 2: for ACol := 0 to COLCOUNT-1 do if (odd(ARow) and odd(ACol)) or odd(ARow+ACol) then begin S := 'Xy' + IntToStr(ARow) + 'x' + IntToStr(ACol); MyWorksheet.WriteUTF8Text(ARow, ACol, S); end else MyWorksheet.WriteNumber(ARow, ACol, 1E5*ARow + ACol); end; end; end; except on E: Exception do Log := Log + format('xxxx ', [(GetTickCount - Tm) / 1000]); end; fname := Trim(Log); fname := CONTENT_PREFIX[RgContent.ItemIndex] + copy(fname, 1, pos(' ', fname)-1); fname := FDir + fname + '_' + IntToStr(idx); if Idx in [2, 4] then Log := Log + ' - ' // No build time in virtual mode else Log := Log + ' ' + format('%5.1f ', [(GetTickCount - Tm) / 1000]); for k := 0 to CgFormats.Items.Count-1 do begin if FEscape then begin Log := 'Test aborted'; exit; end; if not CgFormats.Checked[k] then continue; FCurFormat := SPREAD_FORMAT[k]; StatusMsg('Writing ' + GetFileFormatName(SPREAD_FORMAT[k])); try Application.ProcessMessages; Tm := GetTickCount; MyWorkbook.WriteToFile(fname + FORMAT_EXT[k], SPREAD_FORMAT[k], true); Log := Log + Format('%5.1f ', [(GetTickCount - Tm) / 1000]); except on E: Exception do Log := Log + ' xxxx '; end; end; finally MyWorkbook.Free; Memo.Append(TrimRight(Log)); StatusMsg(''); end; end; procedure TForm1.StatusMsg(const AMsg: String); begin Statusbar.SimpleText := AMsg; Statusbar.Refresh; end; function TForm1.GetRowCount(AIndex: Integer): Integer; var s: String; begin s := CgRowCount.Items[AIndex]; Delete(s, pos('k', s), 99); Result := StrToInt(s) * 1000; end; procedure TForm1.BtnReadClick(Sender: TObject); var i, len: Integer; s: String; rows: Integer; begin WriteToIni; FEscape := false; EnableControls(false); Memo.Append ('Running: Reading TsWorkbook from various file formats'); Memo.Append (' Worksheet contains ' + CONTENT_TEXT[RgContent.ItemIndex]); Memo.Append (' (Times in seconds)'); //'----------- .ods .xlsx biff8 biff5 biff2'); //'Rows x Cols Options Build Write Write Write Write Write' s := '-------------------------------- '; if CgFormats.Checked[fmtODS] then s := s + ' .ods '; if CgFormats.Checked[fmtXLSX] then s := s + '.xlsx '; if CgFormats.Checked[fmtXLS8] then s := s + 'biff8 '; if CgFormats.Checked[fmtXLS5] then s := s + 'biff5 '; if CgFormats.Checked[fmtXLS2] then s := s + 'biff2'; Memo.Append(TrimRight(s)); s := 'Rows x Cols Options '; if CgFormats.Checked[fmtODS] then s := s + ' Read '; if CgFormats.Checked[fmtXLSX] then s := s + ' Read '; if CgFormats.Checked[fmtXLS8] then s := s + ' Read '; if CgFormats.Checked[fmtXLS5] then s := s + ' Read '; if CgFormats.Checked[fmtXLS2] then s := s + ' Read'; s := TrimRight(s); Memo.Append(s); len := Length(s); Memo.Append(DupeString('-', len)); try for i:=0 to CgRowCount.Items.Count-1 do begin if FEscape then exit; if not CgRowCount.Checked[i] then continue; rows := GetRowCount(i); s := Format('%7.0nx%d', [1.0*rows, COLCOUNT]); if CbVirtualModeOnly.Checked then begin RunReadTest(2, s + ' [boVM ]', [boVirtualMode]); RunReadTest(4, s + ' [boVM, boBS]', [boVirtualMode, boBufStream]); end else begin RunReadTest(1, s + ' [ ]', []); RunReadTest(2, s + ' [boVM ]', [boVirtualMode]); RunReadTest(3, s + ' [ boBS]', [boBufStream]); RunReadTest(4, s + ' [boVM, boBS]', [boVirtualMode, boBufStream]); end; Memo.Append(DupeString('-', len)); end; Memo.Append('Ready'); finally Memo.Append(''); EnableControls(true); end; end; procedure TForm1.BtnWriteClick(Sender: TObject); var Rows: integer; s: String; i, len: Integer; begin WriteToIni; FEscape := false; EnableControls(false); Memo.Append ('Running: Building TsWorkbook and writing to different file formats'); Memo.Append (' Worksheet contains ' + CONTENT_TEXT[RgContent.ItemIndex]); Memo.Append (' (Times in seconds)'); //'----------- .ods .xlsx biff8 biff5 biff2'); //'Rows x Cols Options Build Write Write Write Write Write' s := '-------------------------------- '; if CgFormats.Checked[fmtODS] then s := s + ' .ods '; if CgFormats.Checked[fmtXLSX] then s := s + '.xlsx '; if CgFormats.Checked[fmtXLS8] then s := s + 'biff8 '; if CgFormats.Checked[fmtXLS5] then s := s + 'biff5 '; if CgFormats.Checked[fmtXLS2] then s := s + 'biff2'; Memo.Append(TrimRight(s)); s := 'Rows x Cols Options Build '; if CgFormats.Checked[fmtODS] then s := s + 'Write '; if CgFormats.Checked[fmtXLSX] then s := s + 'Write '; if CgFormats.Checked[fmtXLS8] then s := s + 'Write '; if CgFormats.Checked[fmtXLS5] then s := s + 'Write '; if CgFormats.Checked[fmtXLS2] then s := s + 'Write'; s := TrimRight(s); len := Length(s); Memo.Append(s); Memo.Append(DupeString('-', len)); try for i:=0 to CgRowCount.Items.Count-1 do begin if FEscape then exit; if not CgRowCount.Checked[i] then continue; Rows := GetRowCount(i); s := Format('%7.0nx%d', [1.0*Rows, COLCOUNT]); if CbVirtualModeOnly.Checked then begin RunWriteTest(2, Rows, s + ' [boVM ]', [boVirtualMode]); RunWriteTest(4, Rows, s + ' [boVM, boBS]', [boVirtualMode, boBufStream]); end else begin RunWriteTest(1, Rows, s + ' [ ]', []); RunWriteTest(2, Rows, s + ' [boVM ]', [boVirtualMode]); RunWriteTest(3, Rows, s + ' [ boBS]', [boBufStream]); RunWriteTest(4, Rows, s + ' [boVM, boBS]', [boVirtualMode, boBufStream]); end; Memo.Append(DupeString('-', len)); end; Memo.Append('Ready'); finally Memo.Append(''); EnableControls(true); end; end; procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: boolean); begin if CanClose then try WriteToIni; except end; end; procedure TForm1.EnableControls(AEnable: Boolean); begin BtnWrite.Enabled := AEnable; BtnRead.Enabled := AEnable; RgContent.Enabled := AEnable; CgFormats.Enabled := AEnable; CgRowCount.Enabled := AEnable; LblCancel.Visible := not AEnable; StatusMsg(''); Application.ProcessMessages; end; procedure TForm1.FormCreate(Sender: TObject); begin //FDir := GetTempDir; FDir := ExtractFilePath(Application.ExeName) + 'data' + DirectorySeparator; // better than tempdir if you want to look at the files written... if not DirectoryExists(FDir) then CreateDir(FDir); CgFormats.Checked[fmtODS] := true; CgFormats.Checked[fmtXLSX] := true; CgFormats.Checked[fmtXLS8] := true; CgFormats.Checked[fmtXLS5] := true; CgFormats.Checked[fmtXLS2] := true; CgRowCount.Checked[rc10k] := true; CgRowCount.Checked[rc20k] := true; CgRowCount.Checked[rc30k] := true; CgRowCount.Checked[rc40k] := true; ReadFromIni; end; procedure TForm1.FormKeyPress(Sender: TObject; var Key: char); begin if Key = #27 then begin StatusMsg('ESC pressed...'); FEscape := true; end; end; procedure TForm1.ReadFromIni; var ini: TCustomIniFile; n: Byte; begin ini := TMemIniFile.Create(ChangeFileExt(Application.ExeName, '.ini')); try CbVirtualModeOnly.Checked := ini.ReadBool('Parameters', 'VirtualModeOnly', CbVirtualModeOnly.Checked); RgContent.ItemIndex := ini.ReadInteger('Parameters', 'Content', RgContent.ItemIndex); n := Ini.ReadInteger('Parameters', 'Formats', $1F); CgFormats.Checked[fmtODS] := n and $01 <> 0; CgFormats.Checked[fmtXLSX] := n and $02 <> 0; CgFormats.Checked[fmtXLS8] := n and $04 <> 0; CgFormats.Checked[fmtXLS5] := n and $08 <> 0; CgFormats.Checked[fmtXLS2] := n and $10 <> 0; n := Ini.ReadInteger('Parameters', 'RowCount', $0F); CgRowCount.Checked[rc10k] := n and $01 <> 0; CgRowCount.Checked[rc20k] := n and $02 <> 0; CgRowCount.Checked[rc30k] := n and $04 <> 0; CgRowCount.Checked[rc40k] := n and $08 <> 0; CgRowCount.Checked[rc50k] := n and $10 <> 0; CgRowCount.Checked[rc60k] := n and $20 <> 0; CgRowCount.Checked[rc100k]:= n and $40 <> 0; finally ini.Free; end; end; procedure TForm1.WriteToIni; var ini: TMemIniFile; n: Byte; begin ini := TMemIniFile.Create(ChangeFileExt(Application.ExeName, '.ini')); try ini.WriteBool('Parameters', 'VirtualModeOnly', CbVirtualModeOnly.Checked); ini.WriteInteger('Parameters', 'Content', RgContent.ItemIndex); n := 0; if CgFormats.Checked[fmtODS] then n := n or $1; if CgFormats.Checked[fmtXLSX] then n := n or $2; if CgFormats.Checked[fmtXLS8] then n := n or $4; if CgFormats.Checked[fmtXLS5] then n := n or $8; if CgFormats.Checked[fmtXLS2] then n := n or $10; ini.WriteInteger('Parameters', 'Formats', n); n := 0; if CgRowCount.Checked[rc10k] then n := n or $01; if CgRowCount.Checked[rc20k] then n := n or $02; if CgRowCount.Checked[rc30k] then n := n or $04; if CgRowCount.Checked[rc40k] then n := n or $08; if CgRowCount.Checked[rc50k] then n := n or $10; if CgRowCount.Checked[rc60k] then n := n or $20; if CgRowCount.Checked[rc100k] then n := n or $40; ini.WriteInteger('Parameters', 'RowCount', n); finally ini.UpdateFile; ini.Free; end; end; end.