You've already forked lazarus-ccr
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:
@ -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.
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Reference in New Issue
Block a user