fpspreadsheet: Implement virtual mode for all biff file formats. Add record limitations with writer-specific max row and col counts: a file is not written to a particular file format if it exceeds the limitations of the file format.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3307 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-07-11 13:20:14 +00:00
parent e2391c142b
commit 231b127041
10 changed files with 222 additions and 65 deletions

View File

@ -63,7 +63,6 @@
<Unit0> <Unit0>
<Filename Value="test_virtualmode.lpr"/> <Filename Value="test_virtualmode.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="test_virtualmode"/>
</Unit0> </Unit0>
</Units> </Units>
</ProjectOptions> </ProjectOptions>

View File

@ -8,7 +8,7 @@ uses
{$ENDIF}{$ENDIF} {$ENDIF}{$ENDIF}
Classes, laz_fpspreadsheet, Classes, laz_fpspreadsheet,
{ you can add units after this } { you can add units after this }
SysUtils, variants, fpspreadsheet, xlsxooxml; SysUtils, variants, fpspreadsheet, xlsbiff2, xlsbiff5, xlsbiff8, xlsxooxml;
type type
TDataProvider = class TDataProvider = class
@ -35,7 +35,7 @@ type
AData := 10000*ARow + ACol; AData := 10000*ARow + ACol;
// you can use the OnNeedData also to provide feedback on how the process // you can use the OnNeedData also to provide feedback on how the process
// progresses. // progresses:
if (ACol = 0) and (ARow mod 1000 = 0) then if (ACol = 0) and (ARow mod 1000 = 0) then
WriteLn('Writing row ', ARow, '...'); WriteLn('Writing row ', ARow, '...');
end; end;
@ -52,22 +52,30 @@ begin
workbook := TsWorkbook.Create; workbook := TsWorkbook.Create;
try try
worksheet := workbook.AddWorksheet('Sheet1'); worksheet := workbook.AddWorksheet('Sheet1');
worksheet.WriteFontStyle(0, 1, [fssBold]);
{ These are the essential commands to activate virtual mode: } { These are the essential commands to activate virtual mode: }
workbook.WritingOptions := [woVirtualMode, woSaveMemory];
// workbook.WritingOptions := [woVirtualMode, woSaveMemory];
workbook.WritingOptions := [woVirtualMode];
// woSaveMemory can be omitted, but is essential for large files: it causes // woSaveMemory can be omitted, but is essential for large files: it causes
// writing temporaray data to a file stream instead of to a memory stream. // writing temporaray data to a file stream instead of a memory stream.
workbook.VirtualRowCount := 10000; // woSaveMemory, however, considerably slows down writing of biff files.
workbook.VirtualRowCount := 1000;
workbook.VirtualColCount := 100; workbook.VirtualColCount := 100;
// These two numbers define the size of virtual spreadsheet. // These two numbers define the size of virtual spreadsheet.
// In case of a database, VirtualRowCount is the RecordCount, VirtualColCount // In case of a database, VirtualRowCount is the RecordCount, VirtualColCount
// the number of fields to be written to the spreadsheet file // the number of fields to be written to the spreadsheet file
workbook.OnNeedCellData := @dataprovider.NeedCellData; workbook.OnNeedCellData := @dataprovider.NeedCellData;
// This links the worksheet to the method from which it gets the // This links the worksheet to the method from which it gets the
// data to write. // data to write.
// In case of a database, you would open the dataset before calling this: // In case of a database, you would open the dataset before calling this:
workbook.WriteToFile('test_virtual.xlsx', sfOOXML, true); // workbook.WriteToFile('test_virtual.xlsx', sfOOXML, true);
workbook.WriteToFile('test_virtual.xls', sfExcel5, true);
finally finally
workbook.Free; workbook.Free;

View File

@ -23,7 +23,8 @@ type
TOLEDocument = record TOLEDocument = record
// Information about the document // Information about the document
Stream: TMemoryStream; Stream: TStream;
// Stream: TMemoryStream;
end; end;
@ -57,7 +58,7 @@ var
begin begin
VLAbsolutePath:='/'+AStreamName; //Virtual layer always use absolute paths. VLAbsolutePath:='/'+AStreamName; //Virtual layer always use absolute paths.
if not AOverwriteExisting and FileExists(AFileName) then begin if not AOverwriteExisting and FileExists(AFileName) then begin
Raise EStreamError.Createfmt('File already exists "%s"',[AFileName]); Raise EStreamError.Createfmt('File "%s" already exists.',[AFileName]);
end; end;
RealFile:=TFileStream.Create(AFileName,fmCreate); RealFile:=TFileStream.Create(AFileName,fmCreate);
fsOLE:=TVirtualLayer_OLE.Create(RealFile); fsOLE:=TVirtualLayer_OLE.Create(RealFile);
@ -101,7 +102,7 @@ begin
if not Assigned(AOLEDocument.Stream) then begin if not Assigned(AOLEDocument.Stream) then begin
AOLEDocument.Stream:=TMemoryStream.Create; AOLEDocument.Stream:=TMemoryStream.Create;
end else begin end else begin
AOLEDocument.Stream.Clear; (AOLEDocument.Stream as TMemoryStream).Clear;
end; end;
AOLEDocument.Stream.CopyFrom(OLEStream,OLEStream.Size); AOLEDocument.Stream.CopyFrom(OLEStream,OLEStream.Size);
end; end;

View File

@ -2888,6 +2888,10 @@ begin
FPointSeparatorSettings := SysUtils.DefaultFormatSettings; FPointSeparatorSettings := SysUtils.DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator:='.'; FPointSeparatorSettings.DecimalSeparator:='.';
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
FLimitations.MaxCols := 1024;
FLimitations.MaxRows := 1048576;
end; end;
destructor TsSpreadOpenDocWriter.Destroy; destructor TsSpreadOpenDocWriter.Destroy;

View File

@ -21,6 +21,12 @@ type
TsSpreadsheetFormat = (sfExcel2, {sfExcel3, sfExcel4,} sfExcel5, sfExcel8, TsSpreadsheetFormat = (sfExcel2, {sfExcel3, sfExcel4,} sfExcel5, sfExcel8,
sfOOXML, sfOpenDocument, sfCSV, sfWikiTable_Pipes, sfWikiTable_WikiMedia); sfOOXML, sfOpenDocument, sfCSV, sfWikiTable_Pipes, sfWikiTable_WikiMedia);
{@@ Record collection limitations of a particular file format }
TsSpreadsheetFormatLimitations = record
MaxRows: Cardinal;
MaxCols: Cardinal;
end;
const const
{ Default extensions } { Default extensions }
STR_EXCEL_EXTENSION = '.xls'; STR_EXCEL_EXTENSION = '.xls';
@ -738,6 +744,7 @@ type
procedure SetVirtualRowCount(AValue: Cardinal); procedure SetVirtualRowCount(AValue: Cardinal);
{ Internal methods } { Internal methods }
procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal);
procedure PrepareBeforeSaving; procedure PrepareBeforeSaving;
procedure RemoveWorksheetsCallback(data, arg: pointer); procedure RemoveWorksheetsCallback(data, arg: pointer);
procedure UpdateCaches; procedure UpdateCaches;
@ -954,14 +961,19 @@ type
FWorkbook: TsWorkbook; FWorkbook: TsWorkbook;
protected protected
{@@ Limitations for the specific data file format }
FLimitations: TsSpreadsheetFormatLimitations;
{@@ List of number formats found in the workbook. } {@@ List of number formats found in the workbook. }
FNumFormatList: TsCustomNumFormatList; FNumFormatList: TsCustomNumFormatList;
{ Helper routines } { Helper routines }
procedure AddDefaultFormats(); virtual; procedure AddDefaultFormats(); virtual;
procedure CheckLimitations;
procedure CreateNumFormatList; virtual; procedure CreateNumFormatList; virtual;
function ExpandFormula(AFormula: TsFormula): TsExpandedFormula; function ExpandFormula(AFormula: TsFormula): TsExpandedFormula;
function FindFormattingInList(AFormat: PCell): Integer; function FindFormattingInList(AFormat: PCell): Integer;
procedure FixFormat(ACell: PCell); virtual; procedure FixFormat(ACell: PCell); virtual;
procedure GetSheetDimensions(AWorksheet: TsWorksheet;
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual;
procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream);
procedure ListAllFormattingStyles; virtual; procedure ListAllFormattingStyles; virtual;
procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream); procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream);
@ -992,6 +1004,7 @@ type
NextXFIndex: Integer; NextXFIndex: Integer;
constructor Create(AWorkbook: TsWorkbook); virtual; // To allow descendents to override it constructor Create(AWorkbook: TsWorkbook); virtual; // To allow descendents to override it
destructor Destroy; override; destructor Destroy; override;
function Limitations: TsSpreadsheetFormatLimitations;
{ General writing methods } { General writing methods }
procedure IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback); procedure IterateThroughCells(AStream: TStream; ACells: TAVLTree; ACallback: TCellsCallback);
procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); virtual; procedure WriteToFile(const AFileName: string; const AOverwriteExisting: Boolean = False); virtual;
@ -1070,6 +1083,8 @@ resourcestring
lpUnsupportedWriteFormat = 'Tried to write a spreadsheet using an unsupported format'; lpUnsupportedWriteFormat = 'Tried to write a spreadsheet using an unsupported format';
lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file'; lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file';
lpUnknownSpreadsheetFormat = 'unknown format'; lpUnknownSpreadsheetFormat = 'unknown format';
lpMaxRowsExceeded = 'This workbook contains %d rows, but the selected file format does not support more than %d rows.';
lpMaxColsExceeded = 'This workbook contains %d columns, but the selected file format does not support more than %d columns.';
lpInvalidFontIndex = 'Invalid font index'; lpInvalidFontIndex = 'Invalid font index';
lpInvalidNumberFormat = 'Trying to use an incompatible number format.'; lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
lpInvalidDateTimeFormat = 'Trying to use an incompatible date/time format.'; lpInvalidDateTimeFormat = 'Trying to use an incompatible date/time format.';
@ -4084,6 +4099,30 @@ begin
if Result = nil then raise Exception.Create(lpUnsupportedWriteFormat); if Result = nil then raise Exception.Create(lpUnsupportedWriteFormat);
end; end;
{@@
Determines the maximum index of used columns and rows in all sheets of this
workbook. Respects VirtualMode.
Is needed to disable saving when limitations of the format is exceeded. }
procedure TsWorkbook.GetLastRowColIndex(out ALastRow, ALastCol: Cardinal);
var
i: Integer;
sheet: TsWorksheet;
r1,r2, c1,c2: Cardinal;
begin
if (woVirtualMode in WritingOptions) then begin
ALastRow := FVirtualRowCount - 1;
ALastCol := FVirtualColCount - 1;
end else begin
ALastRow := 0;
ALastCol := 0;
for i:=0 to GetWorksheetCount-1 do begin
sheet := GetWorksheetByIndex(i);
ALastRow := Max(ALastRow, sheet.GetLastRowIndex);
ALastCol := Max(ALastCol, sheet.GetLastColIndex);
end;
end;
end;
{@@ {@@
Reads the document from a file. It is assumed to have a given file format. Reads the document from a file. It is assumed to have a given file format.
@ -4221,6 +4260,7 @@ begin
AWriter := CreateSpreadWriter(AFormat); AWriter := CreateSpreadWriter(AFormat);
try try
FFileName := AFileName; FFileName := AFileName;
AWriter.CheckLimitations;
FWriting := true; FWriting := true;
PrepareBeforeSaving; PrepareBeforeSaving;
AWriter.WriteToFile(AFileName, AOverwriteExisting); AWriter.WriteToFile(AFileName, AOverwriteExisting);
@ -4263,6 +4303,7 @@ var
begin begin
AWriter := CreateSpreadWriter(AFormat); AWriter := CreateSpreadWriter(AFormat);
try try
AWriter.CheckLimitations;
FWriting := true; FWriting := true;
PrepareBeforeSaving; PrepareBeforeSaving;
AWriter.WriteToStream(AStream); AWriter.WriteToStream(AStream);
@ -5260,6 +5301,10 @@ begin
inherited Create; inherited Create;
FWorkbook := AWorkbook; FWorkbook := AWorkbook;
CreateNumFormatList; CreateNumFormatList;
{ A good starting point valid for many formats... }
FLimitations.MaxCols := 256;
FLimitations.MaxRows := 65536;
// FNumFormatList.FWorkbook := AWorkbook; // FNumFormatList.FWorkbook := AWorkbook;
end; end;
@ -5347,6 +5392,39 @@ begin
// to be overridden // to be overridden
end; end;
{@@
Returns a record containing limitations of the specific file format of the
writer.
}
function TsCustomSpreadWriter.Limitations: TsSpreadsheetFormatLimitations;
begin
Result := FLimitations;
end;
{@@
Determines the size of the worksheet to be written. VirtualMode is respected.
Is called when the writer needs the size for output.
@param AWorksheet Worksheet to be written
@param AFirsRow Index of first row to be written
@param ALastRow Index of last row
@param AFirstCol Index of first column to be written
@param ALastCol Index of last column to be written
}
procedure TsCustomSpreadWriter.GetSheetDimensions(AWorksheet: TsWorksheet;
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal);
begin
AFirstRow := 0;
AFirstCol := 0;
if (woVirtualMode in AWorksheet.Workbook.WritingOptions) then begin
ALastRow := AWorksheet.Workbook.VirtualRowCount-1;
ALastCol := AWorksheet.Workbook.VirtualColCount-1;
end else begin
ALastRow := AWorksheet.GetLastRowIndex;
ALastCol := AWorksheet.GetLastColIndex;
end;
end;
{@@ {@@
Each descendent should define its own default formats, if any. Each descendent should define its own default formats, if any.
Always add the normal, unformatted style first to speed things up. Always add the normal, unformatted style first to speed things up.
@ -5359,6 +5437,20 @@ begin
NextXFIndex := 0; NextXFIndex := 0;
end; end;
{@@
Checks limitations of the writer, e.g max row/column count
}
procedure TsCustomSpreadWriter.CheckLimitations;
var
lastCol, lastRow: Cardinal;
begin
Workbook.GetLastRowColIndex(lastRow, lastCol);
if lastRow >= FLimitations.MaxRows then
raise Exception.CreateFmt(lpMaxRowsExceeded, [lastRow+1, FLimitations.MaxRows]);
if lastCol >= FLimitations.MaxCols then
raise Exception.CreateFmt(lpMaxColsExceeded, [lastCol+1, FLimitations.MaxCols]);
end;
{@@ {@@
Creates an instance of the number format list which contains prototypes of Creates an instance of the number format list which contains prototypes of
all number formats found in the workbook. all number formats found in the workbook.

View File

@ -981,7 +981,13 @@ begin
WriteXFRecords(AStream); WriteXFRecords(AStream);
WriteColWidths(AStream); WriteColWidths(AStream);
WriteRows(AStream, sheet); WriteRows(AStream, sheet);
WriteCellsToStream(AStream, sheet.Cells);
if (woVirtualMode in Workbook.WritingOptions) then
WriteVirtualCells(AStream)
else begin
WriteRows(AStream, sheet);
WriteCellsToStream(AStream, sheet.Cells);
end;
WriteWindow1(AStream); WriteWindow1(AStream);
// { -- currently not working // { -- currently not working

View File

@ -407,6 +407,7 @@ begin
AStream.Position := CurrentPos; AStream.Position := CurrentPos;
WriteBOF(AStream, INT_BOF_SHEET); WriteBOF(AStream, INT_BOF_SHEET);
WriteIndex(AStream); WriteIndex(AStream);
// WritePageSetup(AStream); // WritePageSetup(AStream);
WriteColInfos(AStream, sheet); WriteColInfos(AStream, sheet);
@ -415,7 +416,14 @@ begin
WritePane(AStream, sheet, true, pane); // true for "is BIFF5 or BIFF8" WritePane(AStream, sheet, true, pane); // true for "is BIFF5 or BIFF8"
WriteSelection(AStream, sheet, pane); WriteSelection(AStream, sheet, pane);
WriteRows(AStream, sheet); WriteRows(AStream, sheet);
WriteCellsToStream(AStream, sheet.Cells);
if (woVirtualMode in Workbook.WritingOptions) then
WriteVirtualCells(AStream)
else begin
WriteRows(AStream, sheet);
WriteCellsToStream(AStream, sheet.Cells);
end;
WriteEOF(AStream); WriteEOF(AStream);
end; end;

View File

@ -358,21 +358,27 @@ end;
procedure TsSpreadBIFF8Writer.WriteToFile(const AFileName: string; procedure TsSpreadBIFF8Writer.WriteToFile(const AFileName: string;
const AOverwriteExisting: Boolean); const AOverwriteExisting: Boolean);
var var
MemStream: TMemoryStream; Stream: TStream;
OutputStorage: TOLEStorage; OutputStorage: TOLEStorage;
OLEDocument: TOLEDocument; OLEDocument: TOLEDocument;
fn: String;
begin begin
MemStream := TMemoryStream.Create; if (woSaveMemory in Workbook.WritingOptions) then begin
fn := GetTempFileName;
Stream := TFileStream.Create(fn, fmCreate + fmOpenRead)
end else
Stream := TMemoryStream.Create;
OutputStorage := TOLEStorage.Create; OutputStorage := TOLEStorage.Create;
try try
WriteToStream(MemStream); WriteToStream(Stream);
// Only one stream is necessary for any number of worksheets // Only one stream is necessary for any number of worksheets
OLEDocument.Stream := MemStream; OLEDocument.Stream := Stream;
OutputStorage.WriteOLEFile(AFileName, OLEDocument, AOverwriteExisting, 'Workbook'); OutputStorage.WriteOLEFile(AFileName, OLEDocument, AOverwriteExisting, 'Workbook');
finally finally
MemStream.Free; Stream.Free;
OutputStorage.Free; OutputStorage.Free;
end; end;
end; end;
@ -439,8 +445,12 @@ begin
WriteDimensions(AStream, sheet); WriteDimensions(AStream, sheet);
//WriteRowAndCellBlock(AStream, sheet); //WriteRowAndCellBlock(AStream, sheet);
WriteRows(AStream, sheet); if (woVirtualMode in Workbook.WritingOptions) then
WriteCellsToStream(AStream, sheet.Cells); WriteVirtualCells(AStream)
else begin
WriteRows(AStream, sheet);
WriteCellsToStream(AStream, sheet.Cells);
end;
WriteWindow2(AStream, sheet); WriteWindow2(AStream, sheet);
WritePane(AStream, sheet, isBIFF8, pane); WritePane(AStream, sheet, isBIFF8, pane);
@ -545,26 +555,26 @@ end;
} }
procedure TsSpreadBIFF8Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet); procedure TsSpreadBIFF8Writer.WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
var var
lLastCol: Word; firstRow, lastRow, firstCol, lastCol: Cardinal;
lLastRow: Integer;
begin begin
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS));
AStream.WriteWord(WordToLE(14)); AStream.WriteWord(WordToLE(14));
{ Determine sheet size }
GetSheetDimensions(AWorksheet, firstRow, lastRow, firstCol, lastCol);
{ Index to first used row } { Index to first used row }
AStream.WriteDWord(DWordToLE(0)); AStream.WriteDWord(DWordToLE(firstRow));
{ Index to last used row, increased by 1 } { Index to last used row, increased by 1 }
lLastRow := GetLastRowIndex(AWorksheet)+1; AStream.WriteDWord(DWordToLE(lastRow+1));
AStream.WriteDWord(DWordToLE(lLastRow)); // Old dummy value: 33
{ Index to first used column } { Index to first used column }
AStream.WriteWord(WordToLE(0)); AStream.WriteWord(WordToLE(firstCol));
{ Index to last used column, increased by 1 } { Index to last used column, increased by 1 }
lLastCol := GetLastColIndex(AWorksheet)+1; AStream.WriteWord(WordToLE(lastCol+1));
AStream.WriteWord(WordToLE(lLastCol)); // Old dummy value: 10
{ Not used } { Not used }
AStream.WriteWord(WordToLE(0)); AStream.WriteWord(WordToLE(0));

View File

@ -508,6 +508,8 @@ type
procedure WriteWindow1(AStream: TStream); virtual; procedure WriteWindow1(AStream: TStream); virtual;
// Writes the index of the XF record used in the given cell // Writes the index of the XF record used in the given cell
procedure WriteXFIndex(AStream: TStream; ACell: PCell); procedure WriteXFIndex(AStream: TStream; ACell: PCell);
// Writes cell content received by workbook in OnNeedCellData event
procedure WriteVirtualCells(AStream: TStream);
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
@ -518,7 +520,7 @@ type
implementation implementation
uses uses
fpsNumFormatParser; Variants, fpsNumFormatParser;
{ Helper table for rpn formulas: { Helper table for rpn formulas:
Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. } Assignment of FormulaElementKinds (fekXXXX) to EXCEL_TOKEN IDs. }
@ -2526,5 +2528,45 @@ begin
AStream.WriteWord(WordToLE(lXFIndex)); AStream.WriteWord(WordToLE(lXFIndex));
end; end;
procedure TsSpreadBIFFWriter.WriteVirtualCells(AStream: TStream);
var
r,c: Cardinal;
lCell: TCell;
value: variant;
begin
FillChar(lCell, SizeOf(lCell), 0);
for r := 0 to Workbook.VirtualRowCount-1 do begin
for c := 0 to Workbook.VirtualColCount-1 do begin
value := varNull;
Workbook.OnNeedCellData(Workbook, r, c, value);
lCell.Row := r;
lCell.Col := c;
if VarIsNull(value) then
lCell.ContentType := cctEmpty
else
if VarIsNumeric(value) then begin
lCell.ContentType := cctNumber;
lCell.NumberValue := value;
end else
{
if VarIsDateTime(value) then begin
lCell.ContentType := cctNumber;
lCell.DateTimeValue := value;
end else
}
if VarIsStr(value) then begin
lCell.ContentType := cctUTF8String;
lCell.UTF8StringValue := VarToStrDef(value, '');
end else
if VarIsBool(value) then begin
lCell.ContentType := cctBool;
lCell.BoolValue := value <> 0;
end else
lCell.ContentType := cctEmpty;
WriteCellCallback(@lCell, AStream);
end;
end;
end;
end. end.

View File

@ -58,6 +58,7 @@ type
{ TsSpreadOOXMLWriter } { TsSpreadOOXMLWriter }
TsSpreadOOXMLWriter = class(TsCustomSpreadWriter) TsSpreadOOXMLWriter = class(TsCustomSpreadWriter)
private
protected protected
FPointSeparatorSettings: TFormatSettings; FPointSeparatorSettings: TFormatSettings;
FSharedStringsCount: Integer; FSharedStringsCount: Integer;
@ -470,6 +471,10 @@ begin
inherited Create(AWorkbook); inherited Create(AWorkbook);
FPointSeparatorSettings := DefaultFormatSettings; FPointSeparatorSettings := DefaultFormatSettings;
FPointSeparatorSettings.DecimalSeparator := '.'; FPointSeparatorSettings.DecimalSeparator := '.';
// http://en.wikipedia.org/wiki/List_of_spreadsheet_software#Specifications
FLimitations.MaxCols := 16384;
FLimitations.MaxRows := 1048576;
end; end;
procedure TsSpreadOOXMLWriter.CreateNumFormatList; procedure TsSpreadOOXMLWriter.CreateNumFormatList;
@ -486,13 +491,13 @@ var
begin begin
if (woSaveMemory in Workbook.WritingOptions) then begin if (woSaveMemory in Workbook.WritingOptions) then begin
dir := IncludeTrailingPathDelimiter(GetTempDir); dir := IncludeTrailingPathDelimiter(GetTempDir);
FSContentTypes := TFileStream.Create(GetTempFileName(dir, 'fpsCT'), fmCreate); FSContentTypes := TFileStream.Create(GetTempFileName(dir, 'fpsCT'), fmCreate+fmOpenRead);
FSRelsRels := TFileStream.Create(GetTempFileName(dir, 'fpsRR'), fmCreate); FSRelsRels := TFileStream.Create(GetTempFileName(dir, 'fpsRR'), fmCreate+fmOpenRead);
FSWorkbookRels := TFileStream.Create(GetTempFileName(dir, 'fpsWBR'), fmCreate); FSWorkbookRels := TFileStream.Create(GetTempFileName(dir, 'fpsWBR'), fmCreate+fmOpenRead);
FSWorkbook := TFileStream.Create(GetTempFileName(dir, 'fpsWB'), fmCreate); FSWorkbook := TFileStream.Create(GetTempFileName(dir, 'fpsWB'), fmCreate+fmOpenRead);
FSStyles := TFileStream.Create(GetTempFileName(dir, 'fpsSTY'), fmCreate); FSStyles := TFileStream.Create(GetTempFileName(dir, 'fpsSTY'), fmCreate+fmOpenRead);
FSSharedStrings := TFileStream.Create(GetTempFileName(dir, 'fpsSST'), fmCreate); FSSharedStrings := TFileStream.Create(GetTempFileName(dir, 'fpsSST'), fmCreate+fmOpenRead);
FSSharedStrings_complete := TFileStream.Create(GetTempFileName(dir, 'fpsSSTc'), fmCreate); FSSharedStrings_complete := TFileStream.Create(GetTempFileName(dir, 'fpsSSTc'), fmCreate+fmOpenRead);
end else begin; end else begin;
FSContentTypes := TMemoryStream.Create; FSContentTypes := TMemoryStream.Create;
FSRelsRels := TMemoryStream.Create; FSRelsRels := TMemoryStream.Create;
@ -507,8 +512,6 @@ end;
{ Destroys the streams that were created by the writer } { Destroys the streams that were created by the writer }
procedure TsSpreadOOXMLWriter.DestroyStreams; procedure TsSpreadOOXMLWriter.DestroyStreams;
var
i: Integer;
procedure DestroyStream(AStream: TStream); procedure DestroyStream(AStream: TStream);
var var
@ -521,6 +524,8 @@ var
AStream.Free; AStream.Free;
end; end;
var
stream: TStream;
begin begin
DestroyStream(FSContentTypes); DestroyStream(FSContentTypes);
DestroyStream(FSRelsRels); DestroyStream(FSRelsRels);
@ -529,40 +534,22 @@ begin
DestroyStream(FSStyles); DestroyStream(FSStyles);
DestroyStream(FSSharedStrings); DestroyStream(FSSharedStrings);
DestroyStream(FSSharedStrings_complete); DestroyStream(FSSharedStrings_complete);
for stream in FSSheets do DestroyStream(stream);
for i := 0 to Length(FSSheets) - 1 do
DestroyStream(FSSheets[i]);
SetLength(FSSheets, 0); SetLength(FSSheets, 0);
end; end;
{ Is called before zipping the individual file parts. Rewinds the memory streams, { Is called before zipping the individual file parts. Rewinds the streams. }
or, if the stream are file streams, the streams are closed and re-opened for
reading. }
procedure TsSpreadOOXMLWriter.ResetStreams; procedure TsSpreadOOXMLWriter.ResetStreams;
var var
i: Integer; stream: TStream;
procedure ResetStream(AStream: TStream);
var
fn: String;
begin
if AStream is TFileStream then begin
fn := TFileStream(AStream).FileName;
AStream.Free;
AStream := TFileStream.Create(fn, fmOpenRead);
end else
AStream.Position := 0;
end;
begin begin
ResetStream(FSContentTypes); FSContentTypes.Position := 0;
ResetStream(FSRelsRels); FSRelsRels.Position := 0;
ResetStream(FSWorkbookRels); FSWorkbookRels.Position := 0;
ResetStream(FSWorkbook); FSWorkbook.Position := 0;
ResetStream(FSStyles); FSStyles.Position := 0;
ResetStream(FSSharedStrings_complete); FSSharedStrings_complete.Position := 0;
for i:=0 to Length(FSSheets) - 1 do for stream in FSSheets do stream.Position := 0;
ResetStream(FSSheets[i]);
end; end;
{ {