fpspreadsheet: Add date/time reading/writing support to BIFF2 and the corresponding "write-read" unit test cases. Passed.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2988 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-05-02 18:49:05 +00:00
parent 5325f81daf
commit 1676d31702
8 changed files with 504 additions and 129 deletions

View File

@ -17,6 +17,9 @@ var
MyWorksheet: TsWorksheet; MyWorksheet: TsWorksheet;
MyRPNFormula: TsRPNFormula; MyRPNFormula: TsRPNFormula;
MyDir: string; MyDir: string;
number: Double;
lCol: TCol;
lRow: TRow;
begin begin
// Open the output file // Open the output file
MyDir := ExtractFilePath(ParamStr(0)); MyDir := ExtractFilePath(ParamStr(0));
@ -30,8 +33,7 @@ begin
// Write some number cells // Write some number cells
MyWorksheet.WriteNumber(0, 0, 1.0); MyWorksheet.WriteNumber(0, 0, 1.0);
MyWorksheet.WriteUsedFormatting(0, 0, [uffBold]); MyWorksheet.WriteUsedFormatting(0, 0, [uffBold, uffNumberFormat]);
MyWorksheet.WriteNumber(0, 1, 2.0); MyWorksheet.WriteNumber(0, 1, 2.0);
MyWorksheet.WriteNumber(0, 2, 3.0); MyWorksheet.WriteNumber(0, 2, 3.0);
MyWorksheet.WriteNumber(0, 3, 4.0); MyWorksheet.WriteNumber(0, 3, 4.0);
@ -95,6 +97,74 @@ begin
MyWorksheet.WriteNumber(6, 3, 2017); MyWorksheet.WriteNumber(6, 3, 2017);
MyWorksheet.WriteFont(6, 3, 'Arial', 18, [fssBold], scBlue); MyWorksheet.WriteFont(6, 3, 'Arial', 18, [fssBold], scBlue);
// Write current date/time to cells B11:B16
MyWorksheet.WriteUTF8Text(10, 0, 'nfShortDate');
MyWorksheet.WriteDateTime(10, 1, now, nfShortDate);
MyWorksheet.WriteUTF8Text(11, 0, 'nfShortTime');
MyWorksheet.WriteDateTime(11, 1, now, nfShortTime);
MyWorksheet.WriteUTF8Text(12, 0, 'nfLongTime');
MyWorksheet.WriteDateTime(12, 1, now, nfLongTime);
MyWorksheet.WriteUTF8Text(13, 0, 'nfShortDateTime');
MyWorksheet.WriteDateTime(13, 1, now, nfShortDateTime);
MyWorksheet.WriteUTF8Text(14, 0, 'nfFmtDateTime, DM');
MyWorksheet.WriteDateTime(14, 1, now, nfFmtDateTime, 'DM');
MyWorksheet.WriteUTF8Text(15, 0, 'nfFmtDateTime, MY');
MyWorksheet.WriteDateTime(15, 1, now, nfFmtDateTime, 'MY');
MyWorksheet.WriteUTF8Text(16, 0, 'nfShortTimeAM');
MyWorksheet.WriteDateTime(16, 1, now, nfShortTimeAM);
MyWorksheet.WriteUTF8Text(17, 0, 'nfLongTimeAM');
MyWorksheet.WriteDateTime(17, 1, now, nfLongTimeAM);
MyWorksheet.WriteUTF8Text(18, 0, 'nfFmtDateTime, MS');
MyWorksheet.WriteDateTime(18, 1, now, nfFmtDateTime, 'MS');
MyWorksheet.WriteUTF8Text(19, 0, 'nfFmtDateTime, MSZ');
MyWorksheet.WriteDateTime(19, 1, now, nfFmtDateTime, 'MSZ');
// Write formatted numbers
number := 12345.67890123456789;
MyWorksheet.WriteUTF8Text(24, 1, '12345.67890123456789');
MyWorksheet.WriteUTF8Text(24, 2, '-12345.67890123456789');
MyWorksheet.WriteUTF8Text(25, 0, 'nfFixed, 0 decs');
MyWorksheet.WriteNumber(25, 1, number, nfFixed, 0);
MyWorksheet.WriteNumber(25, 2, -number, nfFixed, 0);
MyWorksheet.WriteUTF8Text(26, 0, 'nfFixed, 2 decs');
MyWorksheet.WriteNumber(26, 1, number, nfFixed, 2);
MyWorksheet.WriteNumber(26, 2, -number, nfFixed, 2);
MyWorksheet.WriteUTF8Text(27, 0, 'nfFixedTh, 0 decs');
MyWorksheet.WriteNumber(27, 1, number, nfFixedTh, 0);
MyWorksheet.WriteNumber(27, 2, -number, nfFixedTh, 0);
MyWorksheet.WriteUTF8Text(28, 0, 'nfFixedTh, 2 decs');
MyWorksheet.WriteNumber(28, 1, number, nfFixedTh, 2);
MyWorksheet.WriteNumber(28, 2, -number, nfFixedTh, 2);
MyWorksheet.WriteUTF8Text(29, 0, 'nfSci, 1 dec');
MyWorksheet.WriteNumber(29, 1, number, nfSci);
MyWorksheet.WriteNumber(29, 2, -number, nfSci);
MyWorksheet.WriteNumber(29, 3, 1.0/number, nfSci);
MyWorksheet.WriteNumber(29, 4, -1.0/number, nfSci);
MyWorksheet.WriteUTF8Text(30, 0, 'nfExp, 2 decs');
MyWorksheet.WriteNumber(30, 1, number, nfExp, 2);
MyWorksheet.WriteNumber(30, 2, -number, nfExp, 2);
MyWorksheet.WriteNumber(30, 3, 1.0/number, nfExp, 2);
MyWorksheet.WriteNumber(30, 4, -1.0/number, nfExp, 2);
number := 1.333333333;
MyWorksheet.WriteUTF8Text(35, 0, 'nfPercentage, 0 decs');
MyWorksheet.WriteNumber(35, 1, number, nfPercentage, 0);
MyWorksheet.WriteUTF8Text(36, 0, 'nfPercentage, 2 decs');
MyWorksheet.WriteNumber(36, 1, number, nfPercentage, 2);
MyWorksheet.WriteUTF8Text(37, 0, 'nfTimeInterval');
MyWorksheet.WriteDateTime(37, 1, number, nfTimeInterval);
// Set width of columns 0 and 1
MyWorksheet.WriteColWidth(0, 40);
lCol.Width := 35;
MyWorksheet.WriteColInfo(1, lCol);
// Set height of rows 5 and 6
lRow.Height := 10;
MyWorksheet.WriteRowInfo(5, lRow);
lRow.Height := 5;
MyWorksheet.WriteRowInfo(6, lRow);
// Save the spreadsheet to a file // Save the spreadsheet to a file
MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true); MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true);
MyWorkbook.Free; MyWorkbook.Free;

View File

@ -108,7 +108,7 @@
<PackageName Value="LCL"/> <PackageName Value="LCL"/>
</Item2> </Item2>
</RequiredPackages> </RequiredPackages>
<Units Count="27"> <Units Count="28">
<Unit0> <Unit0>
<Filename Value="fpsgrid.lpr"/> <Filename Value="fpsgrid.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
@ -117,7 +117,7 @@
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="1"/> <TopLine Value="1"/>
<CursorPos X="10" Y="7"/> <CursorPos X="10" Y="7"/>
<UsageCount Value="122"/> <UsageCount Value="124"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit0> </Unit0>
<Unit1> <Unit1>
@ -131,21 +131,19 @@
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="43"/> <TopLine Value="43"/>
<CursorPos X="38" Y="63"/> <CursorPos X="38" Y="63"/>
<UsageCount Value="122"/> <UsageCount Value="124"/>
<Loaded Value="True"/> <Loaded Value="True"/>
<LoadedDesigner Value="True"/> <LoadedDesigner Value="True"/>
</Unit1> </Unit1>
<Unit2> <Unit2>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<UnitName Value="fpspreadsheet"/> <UnitName Value="fpspreadsheet"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="4"/> <EditorIndex Value="4"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="2720"/> <TopLine Value="1273"/>
<CursorPos X="49" Y="2742"/> <CursorPos X="28" Y="1285"/>
<UsageCount Value="60"/> <UsageCount Value="60"/>
<Bookmarks Count="1">
<Item0 X="1" Y="1293" ID="1"/>
</Bookmarks>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit2> </Unit2>
<Unit3> <Unit3>
@ -264,11 +262,10 @@
<Unit17> <Unit17>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff8.pas"/>
<UnitName Value="xlsbiff8"/> <UnitName Value="xlsbiff8"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="6"/> <EditorIndex Value="6"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="1988"/> <TopLine Value="1989"/>
<CursorPos X="35" Y="2068"/> <CursorPos X="32" Y="1993"/>
<UsageCount Value="35"/> <UsageCount Value="35"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit17> </Unit17>
@ -292,8 +289,8 @@
<UnitName Value="xlscommon"/> <UnitName Value="xlscommon"/>
<EditorIndex Value="5"/> <EditorIndex Value="5"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="806"/> <TopLine Value="515"/>
<CursorPos X="3" Y="812"/> <CursorPos X="35" Y="541"/>
<UsageCount Value="31"/> <UsageCount Value="31"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit20> </Unit20>
@ -302,8 +299,8 @@
<UnitName Value="xlsbiff5"/> <UnitName Value="xlsbiff5"/>
<EditorIndex Value="7"/> <EditorIndex Value="7"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="1902"/> <TopLine Value="100"/>
<CursorPos X="3" Y="1921"/> <CursorPos X="15" Y="117"/>
<UsageCount Value="18"/> <UsageCount Value="18"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit21> </Unit21>
@ -312,9 +309,12 @@
<UnitName Value="xlsbiff2"/> <UnitName Value="xlsbiff2"/>
<EditorIndex Value="8"/> <EditorIndex Value="8"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="1061"/> <TopLine Value="219"/>
<CursorPos X="3" Y="1057"/> <CursorPos X="26" Y="233"/>
<UsageCount Value="19"/> <UsageCount Value="19"/>
<Bookmarks Count="1">
<Item0 X="21" Y="544" ID="1"/>
</Bookmarks>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit22> </Unit22>
<Unit23> <Unit23>
@ -348,127 +348,137 @@
<CursorPos X="3" Y="143"/> <CursorPos X="3" Y="143"/>
<UsageCount Value="17"/> <UsageCount Value="17"/>
</Unit26> </Unit26>
<Unit27>
<Filename Value="d:\Prog_Delphi\common\units\XLS.pas"/>
<UnitName Value="Xls"/>
<EditorIndex Value="9"/>
<WindowIndex Value="0"/>
<TopLine Value="114"/>
<CursorPos X="42" Y="152"/>
<UsageCount Value="10"/>
<Loaded Value="True"/>
</Unit27>
</Units> </Units>
<JumpHistory Count="30" HistoryIndex="29"> <JumpHistory Count="30" HistoryIndex="29">
<Position1> <Position1>
<Filename Value="..\..\fpspreadsheetgrid.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="272" Column="1" TopLine="246"/> <Caret Line="852" Column="1" TopLine="820"/>
</Position1> </Position1>
<Position2> <Position2>
<Filename Value="..\..\fpspreadsheetgrid.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="273" Column="1" TopLine="246"/> <Caret Line="854" Column="1" TopLine="822"/>
</Position2> </Position2>
<Position3> <Position3>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="2482" Column="52" TopLine="2482"/> <Caret Line="857" Column="1" TopLine="825"/>
</Position3> </Position3>
<Position4> <Position4>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="861" Column="1" TopLine="830"/>
</Position4> </Position4>
<Position5> <Position5>
<Filename Value="..\..\fpspreadsheetgrid.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="273" Column="35" TopLine="212"/> <Caret Line="864" Column="1" TopLine="832"/>
</Position5> </Position5>
<Position6> <Position6>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="189" Column="35" TopLine="189"/> <Caret Line="867" Column="1" TopLine="835"/>
</Position6> </Position6>
<Position7> <Position7>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="869" Column="1" TopLine="837"/>
</Position7> </Position7>
<Position8> <Position8>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="421" Column="17" TopLine="391"/> <Caret Line="1044" Column="1" TopLine="1024"/>
</Position8> </Position8>
<Position9> <Position9>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="2154" Column="6" TopLine="2154"/> <Caret Line="982" Column="1" TopLine="963"/>
</Position9> </Position9>
<Position10> <Position10>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="984" Column="1" TopLine="963"/>
</Position10> </Position10>
<Position11> <Position11>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1581" Column="39" TopLine="1543"/> <Caret Line="957" Column="1" TopLine="938"/>
</Position11> </Position11>
<Position12> <Position12>
<Filename Value="..\..\xlsbiff2.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1076" Column="29" TopLine="1066"/> <Caret Line="958" Column="1" TopLine="938"/>
</Position12> </Position12>
<Position13> <Position13>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="618" Column="27" TopLine="580"/> <Caret Line="960" Column="1" TopLine="938"/>
</Position13> </Position13>
<Position14> <Position14>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="253" Column="37" TopLine="245"/> <Caret Line="962" Column="1" TopLine="938"/>
</Position14> </Position14>
<Position15> <Position15>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="758" Column="1" TopLine="748"/> <Caret Line="968" Column="1" TopLine="938"/>
</Position15> </Position15>
<Position16> <Position16>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="765" Column="47" TopLine="762"/> <Caret Line="1034" Column="1" TopLine="1014"/>
</Position16> </Position16>
<Position17> <Position17>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="351" Column="75" TopLine="329"/> <Caret Line="1037" Column="1" TopLine="1014"/>
</Position17> </Position17>
<Position18> <Position18>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="94" Column="1" TopLine="72"/> <Caret Line="1040" Column="1" TopLine="1014"/>
</Position18> </Position18>
<Position19> <Position19>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1546" Column="31" TopLine="1525"/> <Caret Line="1043" Column="1" TopLine="1014"/>
</Position19> </Position19>
<Position20> <Position20>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1573" Column="37" TopLine="1552"/> <Caret Line="1044" Column="1" TopLine="1014"/>
</Position20> </Position20>
<Position21> <Position21>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1634" Column="38" TopLine="1602"/> <Caret Line="1077" Column="19" TopLine="1064"/>
</Position21> </Position21>
<Position22> <Position22>
<Filename Value="..\..\xlsbiff5.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1813" Column="3" TopLine="1794"/> <Caret Line="536" Column="32" TopLine="530"/>
</Position22> </Position22>
<Position23> <Position23>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="98" Column="1" TopLine="81"/> <Caret Line="124" Column="32" TopLine="97"/>
</Position23> </Position23>
<Position24> <Position24>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1749" Column="3" TopLine="1737"/> <Caret Line="537" Column="24" TopLine="517"/>
</Position24> </Position24>
<Position25> <Position25>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1892" Column="3" TopLine="1874"/> <Caret Line="283" Column="14" TopLine="272"/>
</Position25> </Position25>
<Position26> <Position26>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1903" Column="3" TopLine="1892"/> <Caret Line="124" Column="30" TopLine="104"/>
</Position26> </Position26>
<Position27> <Position27>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="1949" Column="3" TopLine="1933"/> <Caret Line="283" Column="9" TopLine="251"/>
</Position27> </Position27>
<Position28> <Position28>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="d:\Prog_Delphi\common\units\XLS.pas"/>
<Caret Line="1974" Column="3" TopLine="1956"/> <Caret Line="130" Column="20" TopLine="113"/>
</Position28> </Position28>
<Position29> <Position29>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="2068" Column="35" TopLine="2048"/> <Caret Line="290" Column="31" TopLine="238"/>
</Position29> </Position29>
<Position30> <Position30>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlsbiff2.pas"/>
<Caret Line="328" Column="70" TopLine="308"/> <Caret Line="233" Column="26" TopLine="219"/>
</Position30> </Position30>
</JumpHistory> </JumpHistory>
</ProjectOptions> </ProjectOptions>

View File

@ -48,6 +48,7 @@ type
procedure ShowFont; procedure ShowFont;
procedure ShowFooter; procedure ShowFooter;
procedure ShowFormat; procedure ShowFormat;
procedure ShowFormatCount;
procedure ShowFormula; procedure ShowFormula;
procedure ShowHeader; procedure ShowHeader;
procedure ShowHideObj; procedure ShowHideObj;
@ -268,6 +269,8 @@ begin
ShowSelection; ShowSelection;
$001E, $041E: $001E, $041E:
ShowFormat; ShowFormat;
$001F:
ShowFormatCount;
$0022: $0022:
ShowDateMode; ShowDateMode;
$0024: $0024:
@ -1219,6 +1222,21 @@ begin
end; end;
procedure TBIFFGrid.ShowFormatCount;
var
numBytes: Integer;
w: Word;
begin
if FFormat = sfExcel2 then begin
RowCount := 1 + FixedRows;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Number of FORMAT records');
end;
end;
procedure TBIFFGrid.ShowFormula; procedure TBIFFGrid.ShowFormula;
const const
ABS_REL: array[boolean] of string = ('abs', 'rel'); ABS_REL: array[boolean] of string = ('abs', 'rel');

View File

@ -595,11 +595,6 @@ begin
OnRecentFile := @MRUMenuManagerRecentFile; OnRecentFile := @MRUMenuManagerRecentFile;
end; end;
FXFIndex := -1;
FFontIndex := -1;
FFormatIndex := -1;
FRowIndex := -1;
HexGrid.ColWidths[HexGrid.ColCount-1] := 5; HexGrid.ColWidths[HexGrid.ColCount-1] := 5;
HexGrid.DefaultRowHeight := HexGrid.Canvas.TextHeight('Tg') + 4; HexGrid.DefaultRowHeight := HexGrid.Canvas.TextHeight('Tg') + 4;
AlphaGrid.DefaultRowHeight := HexGrid.DefaultRowHeight; AlphaGrid.DefaultRowHeight := HexGrid.DefaultRowHeight;
@ -1059,6 +1054,10 @@ begin
Screen.Cursor := crHourGlass; Screen.Cursor := crHourGlass;
BiffTree.Clear; BiffTree.Clear;
parentnode := nil; parentnode := nil;
FXFIndex := -1;
FFontIndex := -1;
FFormatIndex := -1;
FRowIndex := -1;
AStream.Position := 0; AStream.Position := 0;
while AStream.Position < AStream.Size do begin while AStream.Position < AStream.Size do begin
p := AStream.Position; p := AStream.Position;

View File

@ -208,6 +208,7 @@ type
// One cell per test so some tests can fail and those further below may still work // One cell per test so some tests can fail and those further below may still work
procedure TestWriteReadDates(AFormat: TsSpreadsheetFormat); procedure TestWriteReadDates(AFormat: TsSpreadsheetFormat);
published published
procedure TestWriteReadDates_BIFF2;
procedure TestWriteReadDates_BIFF5; procedure TestWriteReadDates_BIFF5;
procedure TestWriteReadDates_BIFF8; procedure TestWriteReadDates_BIFF8;
end; end;
@ -333,6 +334,11 @@ begin
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
procedure TSpreadWriteReadDateTests.TestWriteReadDates_BIFF2;
begin
TestWriteReadDates(sfExcel2);
end;
procedure TSpreadWriteReadDateTests.TestWriteReadDates_BIFF5; procedure TSpreadWriteReadDateTests.TestWriteReadDates_BIFF5;
begin begin
TestWriteReadDates(sfExcel5); TestWriteReadDates(sfExcel5);

View File

@ -95,6 +95,7 @@ type
// One cell per test so some tests can fail and those further below may still work // One cell per test so some tests can fail and those further below may still work
procedure TestWriteReadNumbers(AFormat: TsSpreadsheetFormat); procedure TestWriteReadNumbers(AFormat: TsSpreadsheetFormat);
published published
procedure TestWriteReadNumbers_BIFF2;
procedure TestWriteReadNumbers_BIFF5; procedure TestWriteReadNumbers_BIFF5;
procedure TestWriteReadNumbers_BIFF8; procedure TestWriteReadNumbers_BIFF8;
end; end;
@ -200,6 +201,11 @@ begin
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
procedure TSpreadWriteReadNumberTests.TestWriteReadNumbers_BIFF2;
begin
TestWriteReadNumbers(sfExcel2);
end;
procedure TSpreadWriteReadNumberTests.TestWriteReadNumbers_BIFF5; procedure TSpreadWriteReadNumberTests.TestWriteReadNumbers_BIFF5;
begin begin
TestWriteReadNumbers(sfExcel5); TestWriteReadNumbers(sfExcel5);

View File

@ -48,6 +48,9 @@ type
procedure ReadRowInfo(AStream: TStream); procedure ReadRowInfo(AStream: TStream);
protected protected
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); override; procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); override;
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String); override;
procedure ReadBlank(AStream: TStream); override; procedure ReadBlank(AStream: TStream); override;
procedure ReadColWidth(AStream: TStream); procedure ReadColWidth(AStream: TStream);
procedure ReadFont(AStream: TStream); procedure ReadFont(AStream: TStream);
@ -57,7 +60,6 @@ type
procedure ReadLabel(AStream: TStream); override; procedure ReadLabel(AStream: TStream); override;
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
procedure ReadXF(AStream: TStream); procedure ReadXF(AStream: TStream);
public public
{ General reading methods } { General reading methods }
@ -77,6 +79,9 @@ type
procedure WriteEOF(AStream: TStream); procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFontIndex: Integer); procedure WriteFont(AStream: TStream; AFontIndex: Integer);
procedure WriteFonts(AStream: TStream); procedure WriteFonts(AStream: TStream);
procedure WriteFormat(AStream: TStream; AFormatCode: String);
procedure WriteFormatCount(AStream: TStream);
procedure WriteFormats(AStream: TStream);
procedure WriteIXFE(AStream: TStream; XFIndex: Word); procedure WriteIXFE(AStream: TStream; XFIndex: Word);
procedure WriteXF(AStream: TStream; AFontIndex, AFormatIndex: byte; procedure WriteXF(AStream: TStream; AFontIndex, AFormatIndex: byte;
ABorders: TsCellBorders = []; AHorAlign: TsHorAlignment = haLeft; ABorders: TsCellBorders = []; AHorAlign: TsHorAlignment = haLeft;
@ -95,7 +100,7 @@ type
end; end;
var var
// the palette of the default BIFF2 colors as "big-endian color" values { the palette of the default BIFF2 colors as "big-endian color" values }
PALETTE_BIFF2: array[$0..$07] of TsColorValue = ( PALETTE_BIFF2: array[$0..$07] of TsColorValue = (
$000000, // $00: black $000000, // $00: black
$FFFFFF, // $01: white $FFFFFF, // $01: white
@ -107,6 +112,36 @@ var
$00FFFF // $07: cyan $00FFFF // $07: cyan
); );
{ These are the built-in number formats of BIFF 2. They are not stored in
the file. Note that, compared to the BUFF5+ built-in formats, two formats
are missing and the indexes are offset by 2 after #11.
It seems that BIFF2 can handle only these 21 formats. The other formats
available in fpspreadsheet are mapped to these 21 formats such that least
destruction is made. }
NUMFORMAT_BIFF2: array[0..20] of string = (
'General', // 0
'0',
'0.00',
'#,##0',
'#,##0.00',
'"$"#,##0_);("$"#,##0)', // 5
'"$"#,##0_);[Red]("$"#,##0)',
'"$"#,##0.00_);("$"#,##0.00)',
'"$"#,##0.00_);[Red]("$"#,##0.00)',
'0%',
'0.00%', // 10
'0.00E+00',
'M/D/YY',
'D-MMM-YY',
'D-MMM',
'MMM-YY', // 15
'h:mm AM/PM',
'h:mm:ss AM/PM',
'h:mm',
'h:mm:ss',
'M/D/YY h:mm' // 20
);
implementation implementation
const const
@ -120,6 +155,7 @@ const
INT_EXCEL_ID_BOF = $0009; INT_EXCEL_ID_BOF = $0009;
INT_EXCEL_ID_EOF = $000A; INT_EXCEL_ID_EOF = $000A;
INT_EXCEL_ID_FORMAT = $001E; INT_EXCEL_ID_FORMAT = $001E;
INT_EXCEL_ID_FORMATCOUNT= $001F;
INT_EXCEL_ID_COLWIDTH = $0024; INT_EXCEL_ID_COLWIDTH = $0024;
INT_EXCEL_ID_XF = $0043; INT_EXCEL_ID_XF = $0043;
INT_EXCEL_ID_IXFE = $0044; INT_EXCEL_ID_IXFE = $0044;
@ -135,6 +171,34 @@ const
INT_EXCEL_CHART = $0020; INT_EXCEL_CHART = $0020;
INT_EXCEL_MACRO_SHEET = $0040; INT_EXCEL_MACRO_SHEET = $0040;
{ FORMAT record constants for BIFF2 }
// Subset of the built-in formats for US Excel,
// including those needed for date/time output
FORMAT_GENERAL = 0; //general/default format
FORMAT_FIXED_0_DECIMALS = 1; //fixed, 0 decimals
FORMAT_FIXED_2_DECIMALS = 2; //fixed, 2 decimals
FORMAT_FIXED_THOUSANDS_0_DECIMALS = 3; //fixed, w/ thousand separator, 0 decs
FORMAT_FIXED_THOUSANDS_2_DECIMALS = 4; //fixed, w/ thousand separator, 2 decs
FORMAT_CURRENCY_0_DECIMALS = 5; //currency (with currency symbol), 0 decs
FORMAT_CURRENCY_2_DECIMALS = 7; //currency (with currency symbol), 2 decs
FORMAT_PERCENT_0_DECIMALS = 9; //percent, 0 decimals
FORMAT_PERCENT_2_DECIMALS = 10; //percent, 2 decimals
FORMAT_EXP_2_DECIMALS = 11; //exponent, 2 decimals
FORMAT_SCI_1_DECIMAL = 11; //scientific, 1 decimal -- not present in BIFF2 -- mapped to EXP_2_DECIMALS
FORMAT_SHORT_DATE = 12; //short date
FORMAT_DATE_DM = 14; //date D-MMM
FORMAT_DATE_MY = 15; //date MMM-YYYY
FORMAT_SHORT_TIME_AM = 16; //short time H:MM with AM
FORMAT_LONG_TIME_AM = 17; //long time H:MM:SS with AM
FORMAT_SHORT_TIME = 18; //short time H:MM
FORMAT_LONG_TIME = 19; //long time H:MM:SS
FORMAT_SHORT_DATETIME = 20; //short date+time
{ The next three formats are not available in BIFF2. They should not be used
when a file is to be saved in BIFF2. If it IS saved as BIFF2 the formats
are mapped to FORMAT_LONG_TIME}
FORMAT_TIME_MS = 19; //time MM:SS
FORMAT_TIME_MSZ = 19; //time MM:SS.0
FORMAT_TIME_INTERVAL = 19; //time [hh]:mm:ss, hh can be >24
{ TsSpreadBIFF2Reader } { TsSpreadBIFF2Reader }
@ -181,6 +245,81 @@ begin
end; end;
end; end;
{ Extracts the number format data from an XF record indexed by AXFIndex.
Note that BIFF2 supports only 21 formats. }
procedure TsSpreadBIFF2Reader.ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String);
const
NOT_USED = nfGeneral;
fmts: array[1..20] of TsNumberFormat = (
nfFixed, nfFixed, nfFixedTh, nfFixedTh, nfFixedTh, // 1..5
nfFixedTh, nfFixedTh, nfFixedTh, nfPercentage, nfPercentage, // 6..10
nfExp, nfShortDate, nfShortDate, nfFmtDateTime, nfFmtDateTime, // 11..15
nfShortTimeAM, nfLongTimeAM, nfShortTime, nfLongTime, nfShortDateTime// 16..20
);
decs: array[1..20] of word = (
0, 2, 0, 2, 0, 0, 2, 2, 0, 2, // 1..10
2, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 11..20
);
var
lFormatData: TFormatListData;
lXFData: TXFListData;
isAMPM: Boolean;
isLongTime: Boolean;
isMilliSec: Boolean;
t,d: Boolean;
begin
ANumberFormat := nfGeneral;
ANumberFormatStr := '';
ADecimals := 0;
lFormatData := FindFormatDataForCell(AXFIndex);
if lFormatData = nil then begin
// no custom format, so first test for default formats
lXFData := TXFListData (FXFList.Items[AXFIndex]);
if (lXFData.FormatIndex > 0) and (lXFData.FormatIndex <= 20) then begin
ANumberFormat := fmts[lXFData.FormatIndex];
ADecimals := decs[lXFData.FormatIndex];
end;
end else
// The next is copied from xlscommon - I think it's not necessary here
if IsPercentNumberFormat(lFormatData.FormatString, ADecimals) then
ANumberFormat := nfPercentage
else
if IsExpNumberFormat(lFormatData.Formatstring, ADecimals) then
ANumberFormat := nfExp
else
if IsThousandSepNumberFormat(lFormatData.FormatString, ADecimals) then
ANumberFormat := nfFixedTh
else
if IsFixedNumberFormat(lFormatData.FormatString, ADecimals) then
ANumberFormat := nfFixed
else begin
t := IsTimeFormat(lFormatData.FormatString, isLongTime, isAMPM, isMilliSec);
d := IsDateFormat(lFormatData.FormatString);
if d and t then
ANumberFormat := nfShortDateTime
else
if d then
ANumberFormat := nfShortDate
else
if t then begin
if isAMPM then begin
if isLongTime then
ANumberFormat := nfLongTimeAM
else
ANumberFormat := nfShortTimeAM;
end else begin
if isLongTime then
ANumberFormat := nfLongTime
else
ANumberFormat := nfShortTime;
end;
end;
end;
end;
procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadBlank(AStream: TStream);
var var
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
@ -341,16 +480,24 @@ procedure TsSpreadBIFF2Reader.ReadNumber(AStream: TStream);
var var
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
XF: Word; XF: Word;
AValue: Double; value: Double;
dt: TDateTime;
nf: TsNumberFormat;
nd: Word;
nfs: String;
begin begin
{ BIFF Record row/column/style } { BIFF Record row/column/style }
ReadRowColXF(AStream, ARow, ACol, XF); ReadRowColXF(AStream, ARow, ACol, XF);
{ IEE 754 floating-point value } { IEE 754 floating-point value }
AStream.ReadBuffer(AValue, 8); AStream.ReadBuffer(value, 8);
{ Save the data } {Find out what cell type, set content type and value}
FWorksheet.WriteNumber(ARow, ACol, AValue); ExtractNumberFormat(XF, nf, nd, nfs);
if IsDateTime(value, nf, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else
FWorksheet.WriteNumber(ARow, ACol, value, nf, nd);
{ Apply formatting to cell } { Apply formatting to cell }
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
@ -414,31 +561,29 @@ begin
end; end;
procedure TsSpreadBIFF2Reader.ReadXF(AStream: TStream); procedure TsSpreadBIFF2Reader.ReadXF(AStream: TStream);
{ { Offset Size Contents
Offset Size Contents 0 1 Index to FONT record (➜5.45)
0 1 Index to FONT record (➜5.45) 1 1 Not used
1 1 Not used 2 1 Number format and cell flags:
2 1 Number format and cell flags: Bit Mask Contents
Bit Mask Contents 5-0 3FH Index to FORMAT record (➜5.49)
5-0 3FH Index to FORMAT record (➜5.49) 6 40H 1 = Cell is locked
6 40H 1 = Cell is locked 7 80H 1 = Formula is hidden
7 80H 1 = Formula is hidden 3 1 Horizontal alignment, border style, and background:
3 1 Horizontal alignment, border style, and background: Bit Mask Contents
Bit Mask Contents 2-0 07H XF_HOR_ALIGN – Horizontal alignment
2-0 07H XF_HOR_ALIGN – Horizontal alignment 0 General, 1 Left, 2 Center, 3 Right, 4 Filled
0 General, 1 Left, 2 Centred, 3 Right, 4 Filled 3 08H 1 = Cell has left black border
3 08H 1 = Cell has left black border 4 10H 1 = Cell has right black border
4 10H 1 = Cell has right black border 5 20H 1 = Cell has top black border
5 20H 1 = Cell has top black border 6 40H 1 = Cell has bottom black border
6 40H 1 = Cell has bottom black border 7 80H 1 = Cell has shaded background }
7 80H 1 = Cell has shaded background
}
type type
TXFRecord = packed record // see p. 224 TXFRecord = packed record
FontIndex: byte; // Offset 0, Size 1 FontIndex: byte;
NotUsed: byte; // Offset 1, Size 1 NotUsed: byte;
NumFormat_Flags: byte; // Offset 2, Size 1 NumFormat_Flags: byte;
HorAlign_Border_BackGround: Byte; // Offset 3, Size 1 HorAlign_Border_BackGround: Byte;
end; end;
var var
lData: TXFListData; lData: TXFListData;
@ -453,7 +598,7 @@ begin
lData.FontIndex := xf.FontIndex; lData.FontIndex := xf.FontIndex;
// Format index // Format index
lData.FormatIndex := xf.NumFormat_Flags and $07; lData.FormatIndex := xf.NumFormat_Flags and $3F;
// Horizontal alignment // Horizontal alignment
b := xf.HorAlign_Border_Background and MASK_XF_HOR_ALIGN; b := xf.HorAlign_Border_Background and MASK_XF_HOR_ALIGN;
@ -490,6 +635,8 @@ begin
// Add the decoded data to the list // Add the decoded data to the list
FXFList.Add(lData); FXFList.Add(lData);
ldata := TXFListData(FXFList.Items[FXFList.Count-1]);
end; end;
@ -635,6 +782,7 @@ begin
WriteBOF(AStream); WriteBOF(AStream);
WriteFonts(AStream); WriteFonts(AStream);
WriteFormats(AStream);
WriteXFRecords(AStream); WriteXFRecords(AStream);
WriteColWidths(AStream); WriteColWidths(AStream);
WriteCellsToStream(AStream, Workbook.GetFirstWorksheet.Cells); WriteCellsToStream(AStream, Workbook.GetFirstWorksheet.Cells);
@ -658,7 +806,7 @@ begin
{ not used } { not used }
AStream.WriteByte(0); AStream.WriteByte(0);
{ number format and cell flags } { Number format index and cell flags }
b := AFormatIndex and $3F; b := AFormatIndex and $3F;
AStream.WriteByte(b); AStream.WriteByte(b);
@ -689,7 +837,7 @@ begin
lFormatIndex := 0; //General format (one of the built-in number formats) lFormatIndex := 0; //General format (one of the built-in number formats)
lBorders := []; lBorders := [];
lHorAlign := FFormattingStyles[i].HorAlignment; lHorAlign := FFormattingStyles[i].HorAlignment;
(*
// Now apply the modifications. // Now apply the modifications.
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then
case FFormattingStyles[i].NumberFormat of case FFormattingStyles[i].NumberFormat of
@ -740,16 +888,18 @@ begin
if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then if (fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy') then
lFormatIndex := FORMAT_DATE_MY lFormatIndex := FORMAT_DATE_MY
else else
{ Because of limitations of BIFF2 the next two formats are mapped
to the same format index! }
if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then if (fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss') then
lFormatIndex := FORMAT_TIME_MS lFormatIndex := FORMAT_TIME_MS
else else
if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then if (fmt = 'msz') or (fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or (fmt = 'mm:ss.0') or (fmt = 'mm:ss.z') or (fmt = 'nn:ss.z') then
lFormatIndex := FORMAT_TIME_MSZ lFormatIndex := FORMAT_TIME_MSZ;
end; end;
nfTimeInterval: nfTimeInterval:
lFormatIndex := FORMAT_TIME_INTERVAL; lFormatIndex := FORMAT_TIME_INTERVAL;
end; end;
*)
if uffBorder in FFormattingStyles[i].UsedFormattingFields then if uffBorder in FFormattingStyles[i].UsedFormattingFields then
lBorders := FFormattingStyles[i].Border; lBorders := FFormattingStyles[i].Border;
@ -880,6 +1030,110 @@ begin
WriteFont(AStream, i); WriteFont(AStream, i);
end; end;
procedure TsSpreadBIFF2Writer.WriteFormat(AStream: TStream; AFormatCode: String);
var
len: Integer;
s: AnsiString;
begin
if AFormatCode = '' then
exit;
s := AFormatCode;
len := Length(s);
{ BIFF record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
AStream.WriteWord(WordToLE(len + 1));
{ Write format string }
AStream.WriteByte(len);
AStream.WriteBuffer(s[1], len);
end;
procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream);
begin
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMATCOUNT));
AStream.WriteWord(WordToLE(2));
AStream.WriteWord(WordToLE(High(NUMFORMAT_BIFF2)+1));
end;
procedure TsSpreadBIFF2Writer.WriteFormats(AStream: TStream);
var
i: Integer;
begin
WriteFormatCount(AStream);
for i:=0 to High(NUMFORMAT_BIFF2) do
WriteFormat(AStream, NUMFORMAT_BIFF2[i]);
end;
(*
var
ds, ts: Char; //decimal separator, thousand separator
begin
ds := DefaultFormatSettings.DecimalSeparator;
ts := DefaultFormatSettings.ThousandSeparator;
{ 0} WriteFormat(AStream, 'General'); // 0
{ 1} WriteFormat(AStream, '0');
{ 2} WriteFormat(AStream, '0'+ds+'00'); // 0.00
{ 3} WriteFormat(AStream, '#'+ts+'##0'); // #,##0
{ 4} WriteFormat(AStream, '#'+ts+'##0'+ds+'00'); // #,##0.00
{ 5} WriteFormat(AStream, '"$"#'+ts+'##0_);("$"#'+ts+'##0)');
{ 6} WriteFormat(AStream, '"$"#'+ts+'##0_);[Red]("$"#'+ts+'##0)');
{ 7} WriteFormat(AStream, '"$"#'+ts+'##0'+ds+'00_);("$"#'+ts+'##0'+ds+'00)');
{ 8} WriteFormat(AStream, '"$"#'+ts+'##0'+ds+'00_);[Red]("$"#'+ts+'##0'+ds+'00)');
{ 9} WriteFormat(AStream, '0%');
{10} WriteFormat(AStream, '0'+ds+'00%'); // 0.00%
{11} WriteFormat(AStream, '0'+ds+'00E+00'); // 0.00E+00
{12} WriteFormat(AStream, 'm/d/yy');
{13} WriteFormat(AStream, 'd-mmm-yy');
{14} WriteFormat(AStream, 'd-mmm');
{15} WriteFormat(AStream, 'mmm-yy');
{16} WriteFormat(AStream, 'h:mm AM/PM');
{17} WriteFormat(AStream, 'h:mm:ss AM/PM');
{18} WriteFormat(AStream, 'h:mm');
{19} WriteFormat(AStream, 'h:mm:ss');
{20} WriteFormat(AStream, 'm/d/yy h:mm');
{ # TODO: locale support
0 => 'GENERAL',
1 => '0',
2 => '0.00',
3 => '#,##0',
4 => '#,##0.00',
5 => '"$"#,##0_);("$"#,##0)',
6 => '"$"#,##0_);[Red]("$"#,##0)',
7 => '"$"#,##0.00_);("$"#,##0.00)',
8 => '"$"#,##0.00_);[Red]("$"#,##0.00)',
9 => '0%',
10 => '0.00%',
11 => '0.00E+00',
12 => '# ?/?',
13 => '# ??/??',
14 => 'M/D/YY',
15 => 'D-MMM-YY',
16 => 'D-MMM',
17 => 'MMM-YY',
18 => 'h:mm AM/PM',
19 => 'h:mm:ss AM/PM',
20 => 'h:mm',
21 => 'h:mm:ss',
22 => 'M/D/YY h:mm',
37 => '_(#,##0_);(#,##0)',
38 => '_(#,##0_);[Red](#,##0)',
39 => '_(#,##0.00_);(#,##0.00)',
40 => '_(#,##0.00_);[Red](#,##0.00)',
41 => '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
42 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
43 => '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
44 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
45 => 'mm:ss',
46 => '[h]:mm:ss',
47 => 'mm:ss.0',
48 => '##0.0E+0',
49 => '@',
}
end;
*)
{ {
Writes an Excel 2 FORMULA record Writes an Excel 2 FORMULA record

View File

@ -207,7 +207,7 @@ const
DATEMODE_1900_BASE=1; //1/1/1900 minus 1 day in FPC TDateTime DATEMODE_1900_BASE=1; //1/1/1900 minus 1 day in FPC TDateTime
DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime DATEMODE_1904_BASE=1462; //1/1/1904 in FPC TDateTime
{ FORMAT record constants } { FORMAT record constants for BIFF5-BIFF8}
// Subset of the built-in formats for US Excel, // Subset of the built-in formats for US Excel,
// including those needed for date/time output // including those needed for date/time output
FORMAT_GENERAL = 0; //general/default format FORMAT_GENERAL = 0; //general/default format
@ -330,7 +330,7 @@ type
// Returns the numberformat for a given XF record // Returns the numberformat for a given XF record
procedure ExtractNumberFormat(AXFIndex: WORD; procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String); out ANumberFormatStr: String); virtual;
// Finds format record for XF record pointed to by cell // Finds format record for XF record pointed to by cell
// Will not return info for built-in formats // Will not return info for built-in formats
function FindFormatDataForCell(const AXFIndex: Integer): TFormatListData; function FindFormatDataForCell(const AXFIndex: Integer): TFormatListData;
@ -382,8 +382,10 @@ type
const AValue: TDateTime; ACell: PCell); override; const AValue: TDateTime; ACell: PCell); override;
// Writes out a PALETTE record containing all colors defined in the workbook // Writes out a PALETTE record containing all colors defined in the workbook
procedure WritePalette(AStream: TStream); procedure WritePalette(AStream: TStream);
public public
constructor Create(AWorkbook: TsWorkbook); override; constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
end; end;
function IsExpNumberFormat(s: String; out Decimals: Word): Boolean; function IsExpNumberFormat(s: String; out Decimals: Word): Boolean;
@ -470,7 +472,7 @@ begin
inherited Destroy; inherited Destroy;
end; end;
{ Applies the XF formatting given by the given index to the specified cell } { Applies the XF formatting referred to by XFIndex to the specified cell }
procedure TsSpreadBIFFReader.ApplyCellFormatting(ARow, ACol: Cardinal; procedure TsSpreadBIFFReader.ApplyCellFormatting(ARow, ACol: Cardinal;
XFIndex: Word); XFIndex: Word);
var var
@ -521,6 +523,8 @@ begin
end; end;
end; end;
{ Extracts number format data from an XF record index by AXFIndex.
Valid for BIFF5-BIFF8. Needs to be overridden for BIFF2 }
procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD; procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Word; out ANumberFormat: TsNumberFormat; out ADecimals: Word;
out ANumberFormatStr: String); out ANumberFormatStr: String);
@ -568,7 +572,7 @@ begin
if lFormatData = nil then begin if lFormatData = nil then begin
// no custom format, so first test for default formats // no custom format, so first test for default formats
lXFData := TXFListData (FXFList.Items[AXFIndex]); lXFData := TXFListData(FXFList.Items[AXFIndex]);
case lXFData.FormatIndex of case lXFData.FormatIndex of
FORMAT_DATE_DM: FORMAT_DATE_DM:
begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end; begin ANumberFormat := nfFmtDateTime; ANumberFormatStr := 'DM'; end;
@ -716,6 +720,9 @@ begin
end; end;
end; end;
{ Read column info (column width) from the stream.
Valid for BIFF3-BIFF8.
For BIFF2 use the records COLWIDTH and COLUMNDEFAULT. }
procedure TsSpreadBiffReader.ReadColInfo(const AStream: TStream); procedure TsSpreadBiffReader.ReadColInfo(const AStream: TStream);
var var
c, c1, c2: Cardinal; c, c1, c2: Cardinal;
@ -768,8 +775,8 @@ procedure TsSpreadBIFFReader.ReadNumber(AStream: TStream);
var var
ARow, ACol: Cardinal; ARow, ACol: Cardinal;
XF: WORD; XF: WORD;
AValue: Double; value: Double;
lDateTime: TDateTime; dt: TDateTime;
nf: TsNumberFormat; nf: TsNumberFormat;
nd: word; nd: word;
nfs: String; nfs: String;
@ -777,14 +784,14 @@ begin
ReadRowColXF(AStream,ARow,ACol,XF); ReadRowColXF(AStream,ARow,ACol,XF);
{ IEE 754 floating-point value } { IEE 754 floating-point value }
AStream.ReadBuffer(AValue, 8); AStream.ReadBuffer(value, 8);
{Find out what cell type, set content type and value} {Find out what cell type, set content type and value}
ExtractNumberFormat(XF, nf, nd, nfs); ExtractNumberFormat(XF, nf, nd, nfs);
if IsDateTime(AValue, nf, lDateTime) then if IsDateTime(value, nf, dt) then
FWorksheet.WriteDateTime(ARow, ACol, lDateTime, nf, nfs) FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else else
FWorksheet.WriteNumber(ARow, ACol, AValue, nf, nd); FWorksheet.WriteNumber(ARow, ACol, value, nf, nd);
{ Add attributes to cell } { Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF); ApplyCellFormatting(ARow, ACol, XF);
@ -853,6 +860,11 @@ begin
FDateMode := dm1900; FDateMode := dm1900;
end; end;
destructor TsSpreadBIFFWriter.Destroy;
begin
inherited Destroy;
end;
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID( function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
AElementKind: TFEKind; out ASecondaryID: Word): Word; AElementKind: TFEKind; out ASecondaryID: Word): Word;
const const
@ -1126,7 +1138,7 @@ procedure TsSpreadBIFFWriter.WriteDateTime(AStream: TStream; const ARow,
var var
ExcelDateSerial: double; ExcelDateSerial: double;
begin begin
ExcelDateSerial := ConvertDateTimeToExcelDateTime(AValue,FDateMode); ExcelDateSerial := ConvertDateTimeToExcelDateTime(AValue, FDateMode);
// fpspreadsheet must already have set formatting to a date/datetime format, so // fpspreadsheet must already have set formatting to a date/datetime format, so
// this will get written out as a pointer to the relevant XF record. // this will get written out as a pointer to the relevant XF record.
// In the end, dates in xls are just numbers with a format. Pass it on to WriteNumber: // In the end, dates in xls are just numbers with a format. Pass it on to WriteNumber: