fpspreadsheet:

- Move BasicReader/Writer from fpspreadsheet.pas to fpsReaderWriter.pas in order to fix circular unit dependence introduced in prev commit. 
- Remove usage of temp file in xlsx and ods stream writers (see forum forum.lazarus.freepascal.org/index.php/topic,30606.msg194818.html).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4398 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-12-02 22:16:55 +00:00
parent c4066180a2
commit a0ee8b8933
5 changed files with 177 additions and 309 deletions

View File

@ -3703,7 +3703,7 @@ begin
{ Now compress the files }
FZip := TZipper.Create;
try
FZip.FileName := '__temp__.tmp';
// FZip.FileName := '__temp__.tmp';
FZip.Entries.AddFileEntry(FSMeta, OPENDOC_PATH_META);
FZip.Entries.AddFileEntry(FSSettings, OPENDOC_PATH_SETTINGS);

View File

@ -40,8 +40,6 @@ type
{ Forward declarations }
TsWorksheet = class;
TsWorkbook = class;
TsBasicSpreadReader = class;
TsBasicSpreadWriter = class;
{**
Type: TRow -- record containing information about a spreadsheet row
@ -637,17 +635,11 @@ type
FCellFormatList: TsCellFormatList;
{ Internal methods }
function CreateSpreadReader(AFormatID: TsSpreadFormatID;
AParams: TsStreamParams = []): TsBasicSpreadReader;
function CreateSpreadWriter(AFormatID: TsSpreadFormatID;
AParams: TsStreamParams = []): TsBasicSpreadWriter;
class function GetFormatFromFileHeader(const AFileName: TFileName;
out AFormatID: TsSpreadFormatID): Boolean; overload;
class function GetFormatFromFileHeader(AStream: TStream;
out AFormatID: TsSpreadFormatID): Boolean; overload;
procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal);
procedure PrepareBeforeReading;
procedure PrepareBeforeSaving;
procedure ReCalc;
@ -775,6 +767,7 @@ type
procedure EnableNotifications;
function NotificationsEnabled: Boolean;
procedure UpdateCaches;
procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal);
{ Error messages }
procedure AddErrorMsg(const AMsg: String); overload;
@ -819,49 +812,6 @@ type
property OnReadCellData: TsWorkbookReadCellDataEvent read FOnReadCellData write FOnReadCellData;
end;
{ TsBasicSpreadReaderWriter }
TsBasicSpreadReaderWriter = class
protected
{@@ Instance of the workbook which is currently being read or written. }
FWorkbook: TsWorkbook;
{@@ Instance of the worksheet which is currently being read or written. }
FWorksheet: TsWorksheet;
{@@ Limitations for the specific data file format }
FLimitations: TsSpreadsheetFormatLimitations;
public
constructor Create(AWorkbook: TsWorkbook); virtual; // to allow descendents to override it
function Limitations: TsSpreadsheetFormatLimitations;
{@@ Instance of the workbook which is currently being read/written. }
property Workbook: TsWorkbook read FWorkbook;
end;
{ TsBasicSpreadReader }
TsBasicSpreadReader = class(TsBasicSpreadReaderWriter)
public
{ General writing methods }
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); virtual; abstract;
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
end;
{ TsBasicSpreadWriter }
TsBasicSpreadWriter = class(TsBasicSpreadReaderWriter)
public
{ Helpers }
procedure CheckLimitations; virtual;
{ General writing methods }
procedure WriteToFile(const AFileName: string;
const AOverwriteExisting: Boolean = False; AParams: TsStreamParams = []); virtual; abstract;
procedure WriteToStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
procedure WriteToStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
end;
{@@ TsSpreadReader class reference type }
TsSpreadReaderClass = class of TsBasicSpreadReader;
{@@ TsSpreadWriter class reference type }
TsSpreadWriterClass = class of TsBasicSpreadWriter;
procedure CopyCellFormat(AFromCell, AToCell: PCell);
@ -870,7 +820,7 @@ implementation
uses
Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser,
fpsStrings, uvirtuallayer_ole,
fpsUtils, fpsHTMLUtils, fpsRegFileFormats, fpsreaderwriter,
fpsUtils, fpsHTMLUtils, fpsRegFileFormats, fpsReaderWriter,
fpsCurrency, fpsExprParser, fpsNumFormatParser;
(*
@ -906,6 +856,67 @@ const
DEF_FONT_AUTOMATIC_COLORVALUE = $000000;
*)
{@@ ----------------------------------------------------------------------------
Convenience method which creates the correct reader object for a given
spreadsheet format.
@param AWorkbook Workbook to be written
@param AFormatID Identifier of the file format which is assumed when reading
a document into the workbook. An exception is raised when
the document has a different format.
@param AParams Optional parameters to control stream access. If contains
the element spClipboard the reader knows that access is to
the clipboard, and it can read a special clipboard version
of the data.
@return An instance of a TsBasicSpreadReader descendent which is able to
read the given file format.
-------------------------------------------------------------------------------}
function CreateSpreadReader(AWorkbook: TsWorkbook; AFormatID: TsSpreadFormatID;
AParams: TsStreamParams = []): TsBasicSpreadReader;
var
readerClass: TsSpreadReaderClass;
begin
Result := nil;
readerClass := GetSpreadReaderClass(AFormatID);
if readerClass <> nil
then Result := readerClass.Create(AWorkbook);
if Result = nil then
raise Exception.Create(rsUnsupportedReadFormat);
end;
{@@ ----------------------------------------------------------------------------
Convenience method which creates the correct writer object for a given
spreadsheet format.
@param AWorkbook Workbook to be written
@param AFormatID Identifier of the file format which is used for writing the
workbook
@param AParams Optional parameters to control stream access. If contains
the element spClipboard then the writer can write a
dedicated clipboard version of the stream if required.
@return An instance of a TsBasicSpreadWriter descendant which is able to
write the given file format.
-------------------------------------------------------------------------------}
function CreateSpreadWriter(AWorkbook: TsWorkbook; AFormatID: TsSpreadFormatID;
AParams: TsStreamParams = []): TsBasicSpreadWriter;
var
writerClass: TsSpreadWriterClass;
begin
Result := nil;
writerClass := GetSpreadWriterClass(AFormatID);
if writerClass <> nil then
Result := writerClass.Create(AWorkbook);
if Result = nil then
raise Exception.Create(rsUnsupportedWriteFormat);
end;
{@@ ----------------------------------------------------------------------------
Copies the format of a cell to another one.
@ -6640,65 +6651,6 @@ begin
end;
end;
{@@ ----------------------------------------------------------------------------
Convenience method which creates the correct reader object for a given
spreadsheet format.
@param AFormatID Identifier of the file format which is assumed when reading
a document into the workbook. An exception is raised when
the document has a different format.
@param AParams Optional parameters to control stream access. If contains
the element spClipboard the reader knows that access is to
the clipboard, and it can read a special clipboard version
of the data.
@return An instance of a TsCustomSpreadReader descendent which is able to
read the given file format.
-------------------------------------------------------------------------------}
function TsWorkbook.CreateSpreadReader(AFormatID: TsSpreadFormatID;
AParams: TsStreamParams = []): TsBasicSpreadReader;
var
readerClass: TsSpreadReaderClass;
begin
Result := nil;
readerClass := GetSpreadReaderClass(AFormatID);
if readerClass <> nil
then Result := readerClass.Create(self);
if Result = nil then
raise Exception.Create(rsUnsupportedReadFormat);
end;
{@@ ----------------------------------------------------------------------------
Convenience method which creates the correct writer object for a given
spreadsheet format.
@param AFormatID Identifier of the file format which is used for writing the
workbook
@param AParams Optional parameters to control stream access. If contains
the element spClipboard then the writer can write a
dedicated clipboard version of the stream if required.
@return An instance of a TsCustomSpreadWriter descendent which is able to
write the given file format.
-------------------------------------------------------------------------------}
function TsWorkbook.CreateSpreadWriter(AFormatID: TsSpreadFormatID;
AParams: TsStreamParams): TsBasicSpreadWriter;
var
writerClass: TsSpreadWriterClass;
begin
Result := nil;
writerClass := GetSpreadWriterClass(AFormatID);
if writerClass <> nil then
Result := writerClass.Create(self);
if Result = nil then
raise Exception.Create(rsUnsupportedWriteFormat);
end;
{@@ ----------------------------------------------------------------------------
Determines the maximum index of used columns and rows in all sheets of this
workbook. Respects VirtualMode.
@ -6761,7 +6713,7 @@ begin
if not FileExists(AFileName) then
raise Exception.CreateFmt(rsFileNotFound, [AFileName]);
AReader := CreateSpreadReader(AFormatID);
AReader := CreateSpreadReader(self, AFormatID);
try
FFileName := AFileName;
PrepareBeforeReading;
@ -6842,119 +6794,6 @@ begin
if not success then
raise Exception.CreateFmt(rsNoValidSpreadsheetFile, [AFileName]);
end;
(*
on E: Exception do
begin
while True do
begin
try
ReadFromFile(AFileName, SheetType, AParams);
canLoad := True;
except
on E: Exception do
begin
if SheetType = _sfExcel8 then lException := E;
canLoad := False
end;
end;
if canLoad or (SheetType = _sfExcel2) then Break;
SheetType := Pred(SheetType);
end;
// A failed attempt to read a file should bring an exception, so re-raise
// the exception if necessary. We re-raise the exception brought by Excel 8,
// since this is the most common format
if (not canLoad) and (lException <> nil) then raise lException;
end
else
ReadFromFile(AFileName, SheetType, AParams);
end else
raise Exception.CreateFmt(rsNoValidSpreadsheetFile, [AFileName]);
for i:=0 to High(fileformats) do
// .xls files can contain several formats. We look into the header first.
if ext = STR_EXCEL_EXTENSION then
begin
canLoad := GetFormatFromFileHeader(AFileName, SheetType);
if canLoad then begin
// Rearrange the file format list such that the format detected from the
// header is first. Must probably the other file formats are not needed,
// just to make sure...
xlsformats := GetSpreadFormatsFromFileName(AFileName, 'BIFF8');
SetLength(fileFormats, Length(xlsformats)+1);
fileFormats[0] := sheetType;
n := 1;
for i := 0 to High(xlsformats) do
if xlsformats[i] <> sheetType then
begin
fileFormats[n] := xlsformats[i];
inc(n);
end;
SetLength(fileformats, n);
end else
begin
// In this case the file format could not be identified from the header.
// But it is possible that this method fails for valid xls files.
// Therefore we open the file explicitly by trial and error using each
// xls reader - see below. We begin with BIFF8 which is most often used.
// Therefore, we read the file format list with BIFF8 at the first item.
fileformats := GetSpreadFormatsFromFileName(AFileName, 'BIFF8');
SheetType := fileFormats[0];
canLoad := true;
end;
end else
begin
SheetType := fileFormats[0];
canLoad := (Length(fileFormats) > 0);
end;
if not canLoad then
raise Exception.CreateFmt(rsReaderNotFound, [AFileName]);
// Here is the trial and error loop checking the file formats with the same extension
for sf := 0 to High(fileFormats) do begin
try
ReadFromFile
if canLoad then
begin
if SheetType = _sfExcel8 then
begin
// Here is the trial-and-error loop checking for the various biff formats.
while True do
begin
try
ReadFromFile(AFileName, SheetType, AParams);
canLoad := True;
except
on E: Exception do
begin
if SheetType = _sfExcel8 then lException := E;
canLoad := False
end;
end;
if canLoad or (SheetType = _sfExcel2) then Break;
SheetType := Pred(SheetType);
end;
// A failed attempt to read a file should bring an exception, so re-raise
// the exception if necessary. We re-raise the exception brought by Excel 8,
// since this is the most common format
if (not canLoad) and (lException <> nil) then raise lException;
end
else
ReadFromFile(AFileName, SheetType, AParams);
end else
raise Exception.CreateFmt(rsNoValidSpreadsheetFile, [AFileName]);
end;
*)
{@@ ----------------------------------------------------------------------------
Reads the document from a file, but ignores the extension.
@ -7008,7 +6847,7 @@ var
AReader: TsBasicSpreadReader;
ok: Boolean;
begin
AReader := CreateSpreadReader(AFormatID);
AReader := CreateSpreadReader(self, AFormatID);
try
PrepareBeforeReading;
FReadWriteFlag := rwfRead;
@ -7082,7 +6921,7 @@ procedure TsWorkbook.WriteToFile(const AFileName: string;
var
AWriter: TsBasicSpreadWriter;
begin
AWriter := CreateSpreadWriter(AFormatID);
AWriter := CreateSpreadWriter(self, AFormatID);
try
FFileName := AFileName;
FFormatID := AFormatID;
@ -7164,7 +7003,7 @@ procedure TsWorkbook.WriteToStream(AStream: TStream;
var
AWriter: TsBasicSpreadWriter;
begin
AWriter := CreateSpreadWriter(AFormatID, AParams);
AWriter := CreateSpreadWriter(self, AFormatID, AParams);
try
FFormatID := AFormatID;
PrepareBeforeSaving;
@ -8437,58 +8276,4 @@ begin
end;
*)
{------------------------------------------------------------------------------}
{ TsBasicSpreadReaderWriter }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Constructor of the reader/writer. Has the workbook to be read/written as a
parameter to apply the localization information found in its FormatSettings.
@param AWorkbook Workbook into which the file is being read or from with the
file is written. This parameter is passed from the workbook
which creates the reader/writer.
-------------------------------------------------------------------------------}
constructor TsBasicSpreadReaderWriter.Create(AWorkbook: TsWorkbook);
begin
inherited Create;
FWorkbook := AWorkbook;
{ A good starting point valid for many formats ... }
FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := MaxInt;
end;
{@@ ----------------------------------------------------------------------------
Returns a record containing limitations of the specific file format of the
writer.
-------------------------------------------------------------------------------}
function TsBasicSpreadReaderWriter.Limitations: TsSpreadsheetFormatLimitations;
begin
Result := FLimitations;
end;
{------------------------------------------------------------------------------}
{ TsBasicSpreadWriter }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Checks limitations of the writer, e.g max row/column count
-------------------------------------------------------------------------------}
procedure TsBasicSpreadWriter.CheckLimitations;
var
lastCol, lastRow: Cardinal;
begin
Workbook.GetLastRowColIndex(lastRow, lastCol);
// Check row count
if lastRow >= FLimitations.MaxRowCount then
Workbook.AddErrorMsg(rsMaxRowsExceeded, [lastRow+1, FLimitations.MaxRowCount]);
// Check column count
if lastCol >= FLimitations.MaxColCount then
Workbook.AddErrorMsg(rsMaxColsExceeded, [lastCol+1, FLimitations.MaxColCount]);
end;
end. {** End Unit: fpspreadsheet }

View File

@ -23,6 +23,50 @@ uses
fpsTypes, fpsClasses, fpSpreadsheet;
type
{ TsBasicSpreadReaderWriter }
TsBasicSpreadReaderWriter = class
protected
{@@ Instance of the workbook which is currently being read or written. }
FWorkbook: TsWorkbook;
{@@ Instance of the worksheet which is currently being read or written. }
FWorksheet: TsWorksheet;
{@@ Limitations for the specific data file format }
FLimitations: TsSpreadsheetFormatLimitations;
public
constructor Create(AWorkbook: TsWorkbook); virtual; // to allow descendents to override it
function Limitations: TsSpreadsheetFormatLimitations;
{@@ Instance of the workbook which is currently being read/written. }
property Workbook: TsWorkbook read FWorkbook;
end;
{ TsBasicSpreadReader }
TsBasicSpreadReader = class(TsBasicSpreadReaderWriter)
public
{ General writing methods }
procedure ReadFromFile(AFileName: string; AParams: TsStreamParams = []); virtual; abstract;
procedure ReadFromStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
procedure ReadFromStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
end;
{ TsBasicSpreadWriter }
TsBasicSpreadWriter = class(TsBasicSpreadReaderWriter)
public
{ Helpers }
procedure CheckLimitations; virtual;
{ General writing methods }
procedure WriteToFile(const AFileName: string;
const AOverwriteExisting: Boolean = False; AParams: TsStreamParams = []); virtual; abstract;
procedure WriteToStream(AStream: TStream; AParams: TsStreamParams = []); virtual; abstract;
procedure WriteToStrings(AStrings: TStrings; AParams: TsStreamParams = []); virtual; abstract;
end;
{@@ TsSpreadReader class reference type }
TsSpreadReaderClass = class of TsBasicSpreadReader;
{@@ TsSpreadWriter class reference type }
TsSpreadWriterClass = class of TsBasicSpreadWriter;
{@@
Custom reader of spreadsheet files. "Custom" means that it provides only
the basic functionality. The main implementation is done in derived classes
@ -141,12 +185,67 @@ implementation
uses
Math,
fpsStrings, fpsUtils, fpsNumFormat, fpsStreams;
fpsStrings, fpsUtils, fpsNumFormat, fpsStreams, fpsRegFileFormats;
{*******************************************************************************
* TsCustomSpreadReader *
*******************************************************************************}
{------------------------------------------------------------------------------}
{ TsBasicSpreadReaderWriter }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Constructor of the reader/writer. Has the workbook to be read/written as a
parameter to apply the localization information found in its FormatSettings.
@param AWorkbook Workbook into which the file is being read or from with the
file is written. This parameter is passed from the workbook
which creates the reader/writer.
-------------------------------------------------------------------------------}
constructor TsBasicSpreadReaderWriter.Create(AWorkbook: TsWorkbook);
begin
inherited Create;
FWorkbook := AWorkbook;
{ A good starting point valid for many formats ... }
FLimitations.MaxColCount := 256;
FLimitations.MaxRowCount := 65536;
FLimitations.MaxPaletteSize := MaxInt;
end;
{@@ ----------------------------------------------------------------------------
Returns a record containing limitations of the specific file format of the
writer.
-------------------------------------------------------------------------------}
function TsBasicSpreadReaderWriter.Limitations: TsSpreadsheetFormatLimitations;
begin
Result := FLimitations;
end;
{------------------------------------------------------------------------------}
{ TsBasicSpreadWriter }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Checks limitations of the writer, e.g max row/column count
-------------------------------------------------------------------------------}
procedure TsBasicSpreadWriter.CheckLimitations;
var
lastCol, lastRow: Cardinal;
begin
Workbook.GetLastRowColIndex(lastRow, lastCol);
// Check row count
if lastRow >= FLimitations.MaxRowCount then
Workbook.AddErrorMsg(rsMaxRowsExceeded, [lastRow+1, FLimitations.MaxRowCount]);
// Check column count
if lastCol >= FLimitations.MaxColCount then
Workbook.AddErrorMsg(rsMaxColsExceeded, [lastCol+1, FLimitations.MaxColCount]);
end;
{------------------------------------------------------------------------------}
{ TsCustomSpreadReader }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Constructor of the reader. Has the workbook to be read as a
@ -335,9 +434,9 @@ begin
end;
{*******************************************************************************
* TsCustomSpreadWriter *
*******************************************************************************}
{------------------------------------------------------------------------------}
{ TsCustomSpreadWriter }
{------------------------------------------------------------------------------}
{@@ ----------------------------------------------------------------------------
Constructor of the writer. Has the workbook to be written as a parameter to
@ -388,23 +487,7 @@ begin
exit;
Result := -1;
end;
(*
{@@ ----------------------------------------------------------------------------
If a color index is greater then the maximum palette color count this
color is replaced by the closest palette color.
The present implementation does not change the color. Must be overridden by
writers of formats with limited palette sizes.
@param AColor Color palette index to be checked
@return Closest color to AColor. If AColor belongs to the palette it must
be returned unchanged.
-------------------------------------------------------------------------------}
function TsCustomSpreadWriter.FixColor(AColor: TsColor): TsColor;
begin
Result := AColor;
end;
*)
{@@ ----------------------------------------------------------------------------
If formatting features of a cell are not supported by the destination file
format of the writer, here is the place to apply replacements.

View File

@ -8,7 +8,7 @@
distribution, for details about the license.
USAGE: Each unit implementing a new spreadsheet format must register the
reader/writer ad some specific data by calling "RegisterSpreadFormat".
reader/writer and some specific data by calling "RegisterSpreadFormat".
-------------------------------------------------------------------------------}
unit fpsRegFileFormats;
@ -17,7 +17,7 @@ unit fpsRegFileFormats;
interface
uses
Classes, SysUtils, fpstypes, fpspreadsheet;
Classes, SysUtils, fpstypes, fpsReaderWriter; //fpspreadsheet;
type
TsSpreadFileAccess = (faRead, faWrite);

View File

@ -3600,7 +3600,7 @@ begin
{ Now compress the files }
FZip := TZipper.Create;
try
FZip.FileName := '__temp__.tmp';
// FZip.FileName := '__temp__.tmp';
FZip.Entries.AddFileEntry(FSContentTypes, OOXML_PATH_TYPES);
FZip.Entries.AddFileEntry(FSRelsRels, OOXML_PATH_RELS_RELS);
FZip.Entries.AddFileEntry(FSWorkbookRels, OOXML_PATH_XL_RELS_RELS);