fpspreadsheet: Starts preparation to support reading dates from Excel 8, not yet functional

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2262 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat
2012-01-23 13:24:13 +00:00
parent 95a37b7c82
commit 37d06a3d25
2 changed files with 180 additions and 9 deletions

View File

@ -85,7 +85,7 @@ type
{@@ Describes the type of content of a cell on a TsWorksheet } {@@ Describes the type of content of a cell on a TsWorksheet }
TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber, TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber,
cctUTF8String); cctUTF8String, cctDateTime);
{@@ List of possible formatting fields } {@@ List of possible formatting fields }
@ -164,6 +164,7 @@ type
RPNFormulaValue: TsRPNFormula; RPNFormulaValue: TsRPNFormula;
NumberValue: double; NumberValue: double;
UTF8StringValue: ansistring; UTF8StringValue: ansistring;
DateTimeValue: TDateTime;
{ Formatting fields } { Formatting fields }
UsedFormattingFields: TsUsedFormattingFields; UsedFormattingFields: TsUsedFormattingFields;
TextRotation: TsTextRotation; TextRotation: TsTextRotation;
@ -202,9 +203,11 @@ type
function GetLastRowNumber: Cardinal; function GetLastRowNumber: Cardinal;
function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring;
function ReadAsNumber(ARow, ACol: Cardinal): Double; function ReadAsNumber(ARow, ACol: Cardinal): Double;
function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean;
procedure RemoveAllCells; procedure RemoveAllCells;
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring);
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double); procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double);
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime);
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula);
procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation); procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation);
@ -259,6 +262,7 @@ type
FWorkbook: TsWorkbook; FWorkbook: TsWorkbook;
FCurrentWorksheet: TsWorksheet; FCurrentWorksheet: TsWorksheet;
public public
constructor Create; virtual;
{ General writing methods } { General writing methods }
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); virtual; procedure ReadFromFile(AFileName: string; AData: TsWorkbook); virtual;
procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); virtual; procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); virtual;
@ -638,6 +642,19 @@ begin
end; end;
end; end;
{@@
Reads the contents of a cell and returns the date/time value of the cell.
@param ARow The row of the cell
@param ACol The column of the cell
@return True if the cell is a datetime value, false otherwise
}
function TsWorksheet.ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean;
begin
end;
{@@ {@@
Clears the list of Cells and releases their memory. Clears the list of Cells and releases their memory.
} }
@ -691,6 +708,16 @@ begin
ACell^.NumberValue := ANumber; ACell^.NumberValue := ANumber;
end; end;
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime);
var
ACell: PCell;
begin
ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctDateTime;
ACell^.DateTimeValue := AValue;
end;
{@@ {@@
Writes a formula to a determined cell Writes a formula to a determined cell
@ -1063,6 +1090,11 @@ end;
{ TsCustomSpreadReader } { TsCustomSpreadReader }
constructor TsCustomSpreadReader.Create;
begin
inherited Create;
end;
{@@ {@@
Default file reading method. Default file reading method.

View File

@ -62,6 +62,16 @@ uses
fpsutils, lazutf8; fpsutils, lazutf8;
type type
TXFRecordData = class
public
FormatIndex: Integer;
end;
TFormatRecordData = class
public
Index: Integer;
FormatString: widestring;
end;
{ TsSpreadBIFF8Reader } { TsSpreadBIFF8Reader }
@ -73,8 +83,11 @@ type
FWorksheetNames: TStringList; FWorksheetNames: TStringList;
FCurrentWorksheet: Integer; FCurrentWorksheet: Integer;
FSharedStringTable: TStringList; FSharedStringTable: TStringList;
FXFList: TFPList; // of TXFRecordData
FFormatList: TFPList; // of TFormatRecordData
function DecodeRKValue(const ARK: DWORD): Double; function DecodeRKValue(const ARK: DWORD): Double;
function ReadWideString(const AStream: TStream;const ALength: WORD): WideString; function ReadWideString(const AStream: TStream;const ALength: WORD): WideString; overload;
function ReadWideString(const AStream: TStream; const AUse8BitLength: Boolean): WideString; overload;
procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook); procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook);
procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook); procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook);
procedure ReadBoundsheet(AStream: TStream); procedure ReadBoundsheet(AStream: TStream);
@ -87,11 +100,18 @@ type
procedure ReadRichString(const AStream: TStream); procedure ReadRichString(const AStream: TStream);
procedure ReadSST(const AStream: TStream); procedure ReadSST(const AStream: TStream);
procedure ReadLabelSST(const AStream: TStream); procedure ReadLabelSST(const AStream: TStream);
//
procedure ReadXF(const AStream: TStream);
procedure ReadFormat(const AStream: TStream);
function FindFormatRecordForCell(const AFXIndex: Integer): TFormatRecordData;
class function ConvertExcelDateToTDateTime(const AExcelDateNum: Integer): TDateTime;
// Workbook Globals records // Workbook Globals records
// procedure ReadCodepage in xlscommon // procedure ReadCodepage in xlscommon
procedure ReadFont(const AStream: TStream); procedure ReadFont(const AStream: TStream);
public public
constructor Create; override;
destructor Destroy; override;
{ General reading methods } { General reading methods }
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override; procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override;
procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); override; procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); override;
@ -99,8 +119,6 @@ type
procedure ReadFormula(AStream: TStream); override; procedure ReadFormula(AStream: TStream); override;
procedure ReadLabel(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
destructor Destroy; override;
end; end;
{ TsSpreadBIFF8Writer } { TsSpreadBIFF8Writer }
@ -164,6 +182,7 @@ const
INT_EXCEL_ID_LABELSST = $00FD; //BIFF8 only INT_EXCEL_ID_LABELSST = $00FD; //BIFF8 only
INT_EXCEL_ID_PALETTE = $0092; INT_EXCEL_ID_PALETTE = $0092;
INT_EXCEL_ID_CODEPAGE = $0042; INT_EXCEL_ID_CODEPAGE = $0042;
INT_EXCEL_ID_FORMAT = $041E;
{ Cell Addresses constants } { Cell Addresses constants }
MASK_EXCEL_ROW = $3FFF; MASK_EXCEL_ROW = $3FFF;
@ -1412,6 +1431,20 @@ begin
end; end;
end; end;
function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
const AUse8BitLength: Boolean): WideString;
var
Len: Word;
WideName: WideString;
begin
if AUse8BitLength then
Len := AStream.ReadByte()
else
Len := WordLEtoN(AStream.ReadWord());
Result := ReadWideString(AStream, Len);
end;
procedure TsSpreadBIFF8Reader.ReadWorkbookGlobals(AStream: TStream; procedure TsSpreadBIFF8Reader.ReadWorkbookGlobals(AStream: TStream;
AData: TsWorkbook); AData: TsWorkbook);
var var
@ -1437,6 +1470,8 @@ begin
INT_EXCEL_ID_SST: ReadSST(AStream); INT_EXCEL_ID_SST: ReadSST(AStream);
INT_EXCEL_ID_CODEPAGE: ReadCodepage(AStream); INT_EXCEL_ID_CODEPAGE: ReadCodepage(AStream);
INT_EXCEL_ID_FONT: ReadFont(AStream); INT_EXCEL_ID_FONT: ReadFont(AStream);
INT_EXCEL_ID_XF: ReadXF(AStream);
INT_EXCEL_ID_FORMAT: ReadFormat(AStream);
else else
// nothing // nothing
end; end;
@ -1521,6 +1556,7 @@ var
RK: DWORD; RK: DWORD;
ARow, ACol, XF: WORD; ARow, ACol, XF: WORD;
Number: Double; Number: Double;
lFormatData: TFormatRecordData;
begin begin
ReadRowColXF(AStream,ARow,ACol,XF); ReadRowColXF(AStream,ARow,ACol,XF);
@ -1529,6 +1565,20 @@ begin
{Check RK codes} {Check RK codes}
Number:=DecodeRKValue(RK); Number:=DecodeRKValue(RK);
// Now try to figure out if the number is really a number of a date or time value
// See: http://www.gaia-gis.it/FreeXL/freexl-1.0.0a-doxy-doc/Format.html
// Unfornately Excel doesnt give us a direct way to find this,
// we need to guess by the FORMAT field
{ lFormatData := FindFormatRecordForCell(XF);
if lFormatData <> nil then
begin
// Dates have /
if Pos('/', lFormatData.FormatString) > 0 then
begin
end;
end;}
FWorksheet.WriteNumber(ARow,ACol,Number); FWorksheet.WriteNumber(ARow,ACol,Number);
end; end;
@ -1616,6 +1666,20 @@ begin
Result:=UTF16ToUTF8(ReadWideString(AStream, ALength)); Result:=UTF16ToUTF8(ReadWideString(AStream, ALength));
end; end;
constructor TsSpreadBIFF8Reader.Create;
begin
inherited Create;
FXFList := TFPList.Create;
FFormatList := TFPList.Create;
end;
destructor TsSpreadBIFF8Reader.Destroy;
begin
FXFList.Free;
FFormatList.Free;
if Assigned(FSharedStringTable) then FSharedStringTable.Free;
end;
procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: string; AData: TsWorkbook); procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
var var
MemStream: TMemoryStream; MemStream: TMemoryStream;
@ -1732,11 +1796,6 @@ begin
FWorksheet.WriteNumber(ARow, ACol, AValue); FWorksheet.WriteNumber(ARow, ACol, AValue);
end; end;
destructor TsSpreadBIFF8Reader.Destroy;
begin
if Assigned(FSharedStringTable) then FSharedStringTable.Free;
end;
procedure TsSpreadBIFF8Reader.ReadRichString(const AStream: TStream); procedure TsSpreadBIFF8Reader.ReadRichString(const AStream: TStream);
var var
L: Word; L: Word;
@ -1829,6 +1888,86 @@ begin
FWorksheet.WriteUTF8Text(ARow, ACol, FSharedStringTable[SSTIndex]); FWorksheet.WriteUTF8Text(ARow, ACol, FSharedStringTable[SSTIndex]);
end; end;
procedure TsSpreadBIFF8Reader.ReadXF(const AStream: TStream);
var
lData: TXFRecordData;
begin
lData := TXFRecordData.Create;
// Record XF, BIFF8:
// Offset Size Contents
// 0 2 Index to FONT record (➜5.45)
WordLEtoN(AStream.ReadWord);
// 2 2 Index to FORMAT record (➜5.49)
lData.FormatIndex := WordLEtoN(AStream.ReadWord);
{4 2 XF type, cell protection, and parent style XF:
Bit Mask Contents
2-0 0007H XF_TYPE_PROT – XF type, cell protection (see above)
15-4 FFF0H Index to parent style XF (always FFFH in style XFs)
6 1 Alignment and text break:
Bit Mask Contents
2-0 07H XF_HOR_ALIGN – Horizontal alignment (see above)
3 08H 1 = Text is wrapped at right border
6-4 70H XF_VERT_ALIGN – Vertical alignment (see above)
7 80H 1 = Justify last line in justified or distibuted text
7 1 XF_ROTATION: Text rotation angle (see above)
8 1 Indentation, shrink to cell size, and text direction:
Bit Mask Contents
3-0 0FH Indent level
4 10H 1 = Shrink content to fit into cell
7-6 C0H Text direction:
0 = According to context
35
; 1 = Left-to-right; 2 = Right-to-left
9 1 Flags for used attribute groups:
....}
// Add the XF to the list
FXFList.Add(lData);
end;
procedure TsSpreadBIFF8Reader.ReadFormat(const AStream: TStream);
var
lData: TFormatRecordData;
begin
lData := TFormatRecordData.Create;
// Record FORMAT, BIFF8:
// Offset Size Contents
// 0 2 Format index used in other records
lData.Index := WordLEtoN(AStream.ReadWord);
// 2 var. Number format string (Unicode string, 16-bit string length, ➜2.5.3)
lData.FormatString := ReadWideString(AStream, False);
// Add to the list
FFormatList.Add(lData);
end;
function TsSpreadBIFF8Reader.FindFormatRecordForCell(const AFXIndex: Integer
): TFormatRecordData;
var
lXFData: TXFRecordData;
lFormatData: TFormatRecordData;
i: Integer;
begin
Result := nil;
lXFData := TXFRecordData(FXFList.Items[AFXIndex]);
for i := 0 to FFormatList.Count-1 do
begin
lFormatData := TFormatRecordData(FFormatList.Items[i]);
if lFormatData.Index = lXFData.FormatIndex then Exit(lFormatData);
end;
end;
class function TsSpreadBIFF8Reader.ConvertExcelDateToTDateTime(
const AExcelDateNum: Integer): TDateTime;
begin
end;
procedure TsSpreadBIFF8Reader.ReadFont(const AStream: TStream); procedure TsSpreadBIFF8Reader.ReadFont(const AStream: TStream);
var var
lCodePage: Word; lCodePage: Word;