fpspreadsheet: Integrate number format parser into fpspreadsheet. Some regressions in format detection...

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3068 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-05-20 16:13:48 +00:00
parent 7c3d710fbe
commit 00ed56d1ef
10 changed files with 574 additions and 264 deletions

View File

@ -29,7 +29,8 @@ var
number: Double; number: Double;
lCell: PCell; lCell: PCell;
lCol: TCol; lCol: TCol;
i, r: Integer; i: Integer;
r: Integer = 10;
begin begin
MyDir := ExtractFilePath(ParamStr(0)); MyDir := ExtractFilePath(ParamStr(0));
@ -53,7 +54,7 @@ begin
} }
// Write some cells // Write some cells
// MyWorksheet.WriteNumber(0, 0, 1.0);// A1 MyWorksheet.WriteDateTime(0, 20, now, nfShortTime); //1.0);// A1
MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1 MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1
MyWorksheet.WriteNumber(0, 1, 2.0);// B1 MyWorksheet.WriteNumber(0, 1, 2.0);// B1
MyWorksheet.WriteNumber(0, 2, 3.0);// C1 MyWorksheet.WriteNumber(0, 2, 3.0);// C1
@ -206,10 +207,6 @@ begin
inc(r); inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz'); MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz'); MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz');
// NOTE: The upper option "MSZ" = "mm:ss.z" should result only in 1 decimal.
// This is true for writing, but in reading always 3 decimals are displayed.
// This is due to fpc's SysUtile.FormatDateTime which does not distinguish
// both cases.
// Write formatted numbers // Write formatted numbers
number := 12345.67890123456789; number := 12345.67890123456789;
@ -252,7 +249,6 @@ begin
MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs'); MyWorksheet.WriteUTF8Text(r, 0, 'nfFixedTh, 3 decs');
MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3);
MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3);
inc(r,2); inc(r,2);
MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec');
MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); MyWorksheet.WriteNumber(r, 1, number, nfSci, 1);

View File

@ -137,8 +137,7 @@
<Unit2> <Unit2>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<UnitName Value="fpspreadsheet"/> <UnitName Value="fpspreadsheet"/>
<IsVisibleTab Value="True"/> <EditorIndex Value="5"/>
<EditorIndex Value="6"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="132"/> <TopLine Value="132"/>
<CursorPos X="16" Y="164"/> <CursorPos X="16" Y="164"/>
@ -148,7 +147,7 @@
<Unit3> <Unit3>
<Filename Value="..\..\fpspreadsheetgrid.pas"/> <Filename Value="..\..\fpspreadsheetgrid.pas"/>
<UnitName Value="fpspreadsheetgrid"/> <UnitName Value="fpspreadsheetgrid"/>
<EditorIndex Value="8"/> <EditorIndex Value="6"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="636"/> <TopLine Value="636"/>
<CursorPos X="20" Y="647"/> <CursorPos X="20" Y="647"/>
@ -286,10 +285,11 @@
<Unit20> <Unit20>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<UnitName Value="xlscommon"/> <UnitName Value="xlscommon"/>
<IsVisibleTab Value="True"/>
<EditorIndex Value="4"/> <EditorIndex Value="4"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="1181"/> <TopLine Value="650"/>
<CursorPos X="31" Y="1194"/> <CursorPos X="16" Y="662"/>
<UsageCount Value="88"/> <UsageCount Value="88"/>
<Bookmarks Count="1"> <Bookmarks Count="1">
<Item0 X="41" Y="1209" ID="1"/> <Item0 X="41" Y="1209" ID="1"/>
@ -577,143 +577,139 @@
</Unit57> </Unit57>
<Unit58> <Unit58>
<Filename Value="d:\lazarus-svn\fpc\2.6.2\source\rtl\objpas\sysutils\dati.inc"/> <Filename Value="d:\lazarus-svn\fpc\2.6.2\source\rtl\objpas\sysutils\dati.inc"/>
<EditorIndex Value="5"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="890"/> <TopLine Value="890"/>
<CursorPos X="16" Y="796"/> <CursorPos X="16" Y="796"/>
<UsageCount Value="15"/> <UsageCount Value="15"/>
<Loaded Value="True"/>
</Unit58> </Unit58>
<Unit59> <Unit59>
<Filename Value="d:\lazarus-svn\fpc\2.6.2\source\rtl\objpas\sysutils\sysinth.inc"/> <Filename Value="d:\lazarus-svn\fpc\2.6.2\source\rtl\objpas\sysutils\sysinth.inc"/>
<EditorIndex Value="7"/>
<WindowIndex Value="0"/> <WindowIndex Value="0"/>
<TopLine Value="36"/> <TopLine Value="36"/>
<CursorPos X="5" Y="43"/> <CursorPos X="5" Y="43"/>
<UsageCount Value="13"/> <UsageCount Value="13"/>
<Loaded Value="True"/>
</Unit59> </Unit59>
</Units> </Units>
<JumpHistory Count="30" HistoryIndex="29"> <JumpHistory Count="30" HistoryIndex="29">
<Position1> <Position1>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="164" Column="60" TopLine="132"/>
</Position1> </Position1>
<Position2> <Position2>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<Caret Line="164" Column="60" TopLine="132"/> <Caret Line="1511" Column="15" TopLine="1478"/>
</Position2> </Position2>
<Position3> <Position3>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1511" Column="15" TopLine="1478"/> <Caret Line="1170" Column="36" TopLine="1170"/>
</Position3> </Position3>
<Position4> <Position4>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1170" Column="36" TopLine="1170"/> <Caret Line="1" Column="1" TopLine="1"/>
</Position4> </Position4>
<Position5> <Position5>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="940" Column="14" TopLine="908"/>
</Position5> </Position5>
<Position6> <Position6>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="940" Column="14" TopLine="908"/> <Caret Line="960" Column="14" TopLine="928"/>
</Position6> </Position6>
<Position7> <Position7>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="960" Column="14" TopLine="928"/> <Caret Line="1013" Column="14" TopLine="982"/>
</Position7> </Position7>
<Position8> <Position8>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1013" Column="14" TopLine="982"/> <Caret Line="1059" Column="14" TopLine="1027"/>
</Position8> </Position8>
<Position9> <Position9>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1059" Column="14" TopLine="1027"/> <Caret Line="1081" Column="41" TopLine="1049"/>
</Position9> </Position9>
<Position10> <Position10>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1081" Column="41" TopLine="1049"/> <Caret Line="1089" Column="43" TopLine="1057"/>
</Position10> </Position10>
<Position11> <Position11>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1089" Column="43" TopLine="1057"/> <Caret Line="1093" Column="14" TopLine="1061"/>
</Position11> </Position11>
<Position12> <Position12>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1093" Column="14" TopLine="1061"/> <Caret Line="1157" Column="14" TopLine="1126"/>
</Position12> </Position12>
<Position13> <Position13>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1157" Column="14" TopLine="1126"/> <Caret Line="1197" Column="20" TopLine="1177"/>
</Position13> </Position13>
<Position14> <Position14>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1197" Column="20" TopLine="1177"/> <Caret Line="403" Column="16" TopLine="397"/>
</Position14> </Position14>
<Position15> <Position15>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="403" Column="16" TopLine="397"/> <Caret Line="924" Column="14" TopLine="893"/>
</Position15> </Position15>
<Position16> <Position16>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="924" Column="14" TopLine="893"/> <Caret Line="413" Column="19" TopLine="402"/>
</Position16> </Position16>
<Position17> <Position17>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="413" Column="19" TopLine="402"/> <Caret Line="414" Column="33" TopLine="388"/>
</Position17> </Position17>
<Position18> <Position18>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="414" Column="33" TopLine="388"/> <Caret Line="854" Column="39" TopLine="825"/>
</Position18> </Position18>
<Position19> <Position19>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="854" Column="39" TopLine="825"/> <Caret Line="672" Column="1" TopLine="648"/>
</Position19> </Position19>
<Position20> <Position20>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="672" Column="1" TopLine="648"/> <Caret Line="870" Column="1" TopLine="832"/>
</Position20> </Position20>
<Position21> <Position21>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="870" Column="1" TopLine="832"/> <Caret Line="867" Column="24" TopLine="848"/>
</Position21> </Position21>
<Position22> <Position22>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="867" Column="24" TopLine="848"/> <Caret Line="926" Column="34" TopLine="907"/>
</Position22> </Position22>
<Position23> <Position23>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="926" Column="34" TopLine="907"/> <Caret Line="956" Column="1" TopLine="937"/>
</Position23> </Position23>
<Position24> <Position24>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="956" Column="1" TopLine="937"/> <Caret Line="1024" Column="63" TopLine="1006"/>
</Position24> </Position24>
<Position25> <Position25>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<Caret Line="1024" Column="63" TopLine="1006"/> <Caret Line="1478" Column="48" TopLine="1478"/>
</Position25> </Position25>
<Position26> <Position26>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<Caret Line="1478" Column="48" TopLine="1478"/> <Caret Line="1" Column="1" TopLine="1"/>
</Position26> </Position26>
<Position27> <Position27>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1" Column="1" TopLine="1"/> <Caret Line="1058" Column="9" TopLine="1039"/>
</Position27> </Position27>
<Position28> <Position28>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1058" Column="9" TopLine="1039"/> <Caret Line="1055" Column="44" TopLine="1039"/>
</Position28> </Position28>
<Position29> <Position29>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<Caret Line="1055" Column="44" TopLine="1039"/> <Caret Line="1135" Column="22" TopLine="1120"/>
</Position29> </Position29>
<Position30> <Position30>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<Caret Line="1135" Column="22" TopLine="1120"/> <Caret Line="164" Column="16" TopLine="132"/>
</Position30> </Position30>
</JumpHistory> </JumpHistory>
</ProjectOptions> </ProjectOptions>
@ -743,15 +739,6 @@
</Other> </Other>
</CompilerOptions> </CompilerOptions>
<Debugging> <Debugging>
<BreakPoints Count="1">
<Item1>
<Kind Value="bpkSource"/>
<WatchScope Value="wpsLocal"/>
<WatchKind Value="wpkWrite"/>
<Source Value="..\..\xlscommon.pas"/>
<Line Value="664"/>
</Item1>
</BreakPoints>
<Watches Count="2"> <Watches Count="2">
<Item1> <Item1>
<Expression Value="recordtype"/> <Expression Value="recordtype"/>

View File

@ -27,6 +27,8 @@ type
coEqual, coNotEqual, coLess, coGreater, coLessEqual, coGreaterEqual coEqual, coNotEqual, coLess, coGreater, coLessEqual, coGreaterEqual
); );
TsConversionDirection = (cdToFPSpreadsheet, cdFromFPSpreadsheet);
TsNumFormatSection = record TsNumFormatSection = record
FormatString: String; FormatString: String;
CompareOperation: TsCompareOperation; CompareOperation: TsCompareOperation;
@ -38,17 +40,23 @@ type
NumFormat: TsNumberFormat; NumFormat: TsNumberFormat;
end; end;
TsNumFormatSections = array of TsNumFormatSection;
TsNumFormatParser = class TsNumFormatParser = class
private private
FCreateMethod: Byte;
FWorkbook: TsWorkbook; FWorkbook: TsWorkbook;
FCurrent: PChar; FCurrent: PChar;
FStart: PChar; FStart: PChar;
FEnd: PChar; FEnd: PChar;
FCurrSection: Integer; FCurrSection: Integer;
FSections: array of TsNumFormatSection; FSections: TsNumFormatSections;
FFormatSettings: TFormatSettings; FFormatSettings: TFormatSettings;
FFormatString: String; FFormatString: String;
FNumFormat: TsNumberFormat;
FConversionDirection: TsConversionDirection;
FStatus: Integer; FStatus: Integer;
function GetFormatString: String;
function GetParsedSectionCount: Integer; function GetParsedSectionCount: Integer;
function GetParsedSections(AIndex: Integer): TsNumFormatSection; function GetParsedSections(AIndex: Integer): TsNumFormatSection;
@ -58,6 +66,8 @@ type
procedure AnalyzeBracket(const AValue: String); procedure AnalyzeBracket(const AValue: String);
procedure AnalyzeText(const AValue: String); procedure AnalyzeText(const AValue: String);
procedure CheckSections; procedure CheckSections;
function CreateFormatStringFromSection(ASection: Integer): String; virtual;
function CreateFormatStringFromSections: String;
procedure Parse(const AFormatString: String); procedure Parse(const AFormatString: String);
procedure ScanAMPM(var s: String); procedure ScanAMPM(var s: String);
procedure ScanBrackets; procedure ScanBrackets;
@ -68,9 +78,16 @@ type
procedure ScanText; procedure ScanText;
public public
constructor Create(AWorkbook: TsWorkbook; const AFormatString: String); constructor Create(AWorkbook: TsWorkbook; const AFormatString: String;
AConversionDirection: TsConversionDirection = cdToFPSpreadsheet); overload;
constructor Create(AWorkbook: TsWorkbook; const AFormatSections: TsNumFormatSections;
AConversionDirection: TsConversionDirection = cdFromFPSpreadsheet); overload;
destructor Destroy; override; destructor Destroy; override;
property FormatString: String read FFormatString; procedure CopySections(const FromSections: TsNumFormatSections;
var ToSections: TsNumFormatSections);
procedure CopySectionsTo(var ADestination: TsNumFormatSections);
property Builtin_NumFormat: TsNumberFormat read FNumFormat;
property FormatString: String read GetFormatString;
property ParsedSectionCount: Integer read GetParsedSectionCount; property ParsedSectionCount: Integer read GetParsedSectionCount;
property ParsedSections[AIndex: Integer]: TsNumFormatSection read GetParsedSections; property ParsedSections[AIndex: Integer]: TsNumFormatSection read GetParsedSections;
property Status: Integer read FStatus; property Status: Integer read FStatus;
@ -88,10 +105,15 @@ const
{ TsNumFormatParser } { TsNumFormatParser }
{ Creates a number format parser for analyzing a formatstring that has been read
from a spreadsheet file. The conversion, by default, will go FROM the file TO
the fpspreadsheet procedures. }
constructor TsNumFormatParser.Create(AWorkbook: TsWorkbook; constructor TsNumFormatParser.Create(AWorkbook: TsWorkbook;
const AFormatString: String); const AFormatString: String; AConversionDirection: TsConversionDirection = cdToFPSpreadsheet);
begin begin
inherited Create; inherited Create;
FCreateMethod := 0;
FConversionDirection := AConversionDirection;
FWorkbook := AWorkbook; FWorkbook := AWorkbook;
FFormatSettings := DefaultFormatSettings; FFormatSettings := DefaultFormatSettings;
FFormatSettings.DecimalSeparator := '.'; FFormatSettings.DecimalSeparator := '.';
@ -99,6 +121,22 @@ begin
Parse(AFormatString); Parse(AFormatString);
end; end;
{ Creates a number format parser to create a format string from the individual
format sections given in "AFormatSections". It is assumed by default that the
format string will be written to file. Therefore, it can contain features of
the destination file format and, in general, will not work if called by
fpspreadsheet. }
constructor TsNumFormatParser.Create(AWorkbook: TsWorkbook;
const AFormatSections: TsNumFormatSections;
AConversionDirection: TsConversionDirection = cdFromFPSpreadsheet);
begin
inherited Create;
FCreateMethod := 1;
FConversionDirection := AConversionDirection;
FWorkbook := AWorkbook;
CopySections(AFormatSections, FSections);
end;
destructor TsNumFormatParser.Destroy; destructor TsNumFormatParser.Destroy;
begin begin
FSections := nil; FSections := nil;
@ -119,7 +157,7 @@ begin
FormatString := ''; FormatString := '';
CompareOperation := coNotUsed; CompareOperation := coNotUsed;
CompareValue := 0.0; CompareValue := 0.0;
Color := scBlack; Color := scNotDefined;
CountryCode := ''; CountryCode := '';
CurrencySymbol := ''; CurrencySymbol := '';
Decimals := 0; Decimals := 0;
@ -221,6 +259,7 @@ begin
exit; exit;
end; end;
// Check format strings
case FSections[i].NumFormat of case FSections[i].NumFormat of
nfGeneral, nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci, nfCurrency: nfGeneral, nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci, nfCurrency:
try try
@ -241,6 +280,29 @@ begin
end; end;
end; end;
// Extract built-in NumFormat identifier for currency (needs several entries in
// three sections).
if (ns = 3) and
(FSections[0].NumFormat = nfCurrency) and
(FSections[1].NumFormat = nfCurrency) and
(FSections[2].NumFormat = nfCurrency)
then begin
if ((FSections[2].FormatString = '-') or (FSections[2].FormatString = '"-"')) then begin
if (FSections[1].Color = scRed) then
FNumFormat := nfCurrencyDashRed
else
FNumFormat := nfCurrencyDash;
end else begin
if (FSections[1].Color = scRed) then
FNumFormat := nfCurrencyRed;
end;
end else
// If there are other multi-section formatstrings they must be a custom format
if (ns > 1) then
FNumFormat := nfCustom
else
FNumFormat := FSections[0].NumFormat;
if ns = 2 then if ns = 2 then
FFormatString := Format('%s;%s;%s', [ FFormatString := Format('%s;%s;%s', [
FSections[0].FormatString, FSections[0].FormatString,
@ -256,33 +318,79 @@ begin
FStatus := psErrNoUsableFormat; FStatus := psErrNoUsableFormat;
end; end;
{ procedure TsNumFormatParser.CopySections(
function TsNumFormatParser.GetNumFormat: TsNumberFormat; const FromSections: TsNumFormatSections; var ToSections: TsNumformatSections);
var var
i: Integer; i: Integer;
begin begin
if FStatus <> psOK then SetLength(ToSections, Length(FromSections));
Result := nfGeneral for i:= 0 to High(FromSections) do begin
else ToSections[i].FormatString := FromSections[i].FormatString;
if (FSections[0].NumFormat = nfCurrency) and (FSections[1].NumFormat = nfCurrency) and ToSections[i].CompareOperation := FromSections[i].CompareOperation;
(FSections[2].NumFormat = nfCurrency) ToSections[i].CompareValue := FromSections[i].CompareValue;
then begin ToSections[i].Color := FromSections[i].Color;
if (FSections[1].Color = scNotDefined) then begin ToSections[i].CurrencySymbol := FromSections[i].CurrencySymbol;
if (FSections[2].FormatString = '-') then ToSections[i].Decimals := FromSections[i].Decimals;
Result := nfCurrencyDash ToSections[i].NumFormat := FromSections[i].NumFormat;
else end;
Result := nfCurrency; end;
end else
if FSections[1].Color = scRed then begin procedure TsNumFormatParser.CopySectionsTo(var ADestination: TsNumFormatSections);
if (FSections[2].Formatstring = '-') then begin
Result := nfCurrencyDashRed CopySections(FSections, ADestination);
else end;
Result := nfCurrencyRed;
function TsNumFormatParser.CreateFormatStringFromSections: String;
var
i: Integer;
begin
if Length(FSections) = 0 then
Result := ''
else begin
Result := CreateFormatStringFromSection(0);
for i:=1 to High(FSections) do
Result := Result + ';' + CreateFormatStringFromSection(i);
end;
end;
function TsNumFormatParser.CreateFormatStringFromSection(ASection: Integer): String;
begin
with FSections[ASection] do
if (NumFormat = nfFmtDateTime) or (NumFormat = nfCustom) then begin
Result := FormatString;
exit;
end;
Result := BuildNumberFormatString(FSections[ASection].NumFormat,
FWorkbook.FormatSettings,
FSections[ASection].Decimals,
FSections[ASection].CurrencySymbol
);
if FConversionDirection = cdFromFPSpreadsheet then begin
// This is typical of Excel, but is valid for all others as well.
// Override if you need to change
if FSections[ASection].Color < 8 then
Result := Format('[%s]%s', [FWorkbook.GetColorName(FSections[ASection].Color), Result])
else
if FSections[ASection].Color < scNotDefined then
Result := Format('[Color%d]%s', [FSections[ASection].Color, Result]);
if FSections[ASection].CompareOperation <> coNotUsed then
Result := Format('[%s%g]%s', [
COMPARE_STR[FSections[ASection].CompareOperation],
FSections[ASection].CompareValue,
Result
]);
end;
end;
function TsNumFormatParser.GetFormatString: String;
begin
case FCreateMethod of
0: Result := FFormatString;
1: Result := CreateFormatStringFromSections;
end; end;
end else
Result := FSections[0].NumFormat;
end; end;
}
function TsNumFormatParser.GetParsedSectionCount: Integer; function TsNumFormatParser.GetParsedSectionCount: Integer;
begin begin
@ -355,39 +463,51 @@ begin
while (FCurrent <= FEnd) and (FStatus = psOK) and (not done) do begin while (FCurrent <= FEnd) and (FStatus = psOK) and (not done) do begin
token := FCurrent^; token := FCurrent^;
case token of case token of
'\' : begin '\':
begin
inc(FCurrent); inc(FCurrent);
token := FCurrent^; token := FCurrent^;
s := s + token; s := s + token;
end; end;
'Y', 'y' : begin 'Y', 'y':
begin
ScanDateTimeParts(token, token, s); ScanDateTimeParts(token, token, s);
isTime := false; isTime := false;
end; end;
'M', 'm' : if isTime then // help fpc to separate "month" and "minute" 'M', 'm':
ScanDateTimeParts(token, token, s);
{if isTime then // help fpc to separate "month" and "minute"
ScanDateTimeParts(token, 'n', s) ScanDateTimeParts(token, 'n', s)
else // both "month" and "minute" work in fpc to some degree else // both "month" and "minute" work in fpc to some degree
ScanDateTimeParts(token, token, s); ScanDateTimeParts(token, token, s);}
'D', 'd' : begin 'N', 'n':
ScanDateTimeParts(token, 'n', s); // fpc dialect for "minutes"
'D', 'd':
begin
ScanDateTimeParts(token, token, s); ScanDateTimeParts(token, token, s);
isTime := false; isTime := false;
end; end;
'H', 'h' : begin 'H', 'h':
begin
ScanDateTimeParts(token, token, s); ScanDateTimeParts(token, token, s);
isTime := true; isTime := true;
end; end;
'S', 's' : begin 'S', 's':
begin
ScanDateTimeParts(token, token, s); ScanDateTimeParts(token, token, s);
isTime := true; isTime := true;
end; end;
'/', ':', '.', ']', '[', ' ' '/', ':', '.', ']', '[', ' ':
: s := s + token; s := s + token;
'0' : ScanDateTimeParts(token, 'z', s); '0', 'z', 'Z':
'A', 'a' : begin ScanDateTimeParts(token, token, s);
'A', 'a':
begin
ScanAMPM(s); ScanAMPM(s);
isAMPM := true; isAMPM := true;
end; end;
else begin else
begin
done := true; done := true;
dec(FCurrent); dec(FCurrent);
// char pointer must be at end of date/time mask. // char pointer must be at end of date/time mask.
@ -398,31 +518,22 @@ begin
FSections[FCurrSection].FormatString := FSections[FCurrSection].FormatString + s; FSections[FCurrSection].FormatString := FSections[FCurrSection].FormatString + s;
s := FSections[FCurrSection].FormatString; s := FSections[FCurrSection].FormatString;
// Check format
try
if s <> '' then begin if s <> '' then begin
FormatDateTime(s, now);
// !!!! MODIFY TO USE EXTENDED SYNTAX !!!!!
if s = FWorkbook.FormatSettings.LongDateFormat then if s = FWorkbook.FormatSettings.LongDateFormat then
nf := nfLongDate nf := nfLongDate
else else
if s = FWorkbook.FormatSettings.ShortDateFormat then if s = FWorkbook.FormatSettings.ShortDateFormat then
nf := nfShortDate nf := nfShortDate
else else
if s = FWorkbook.FormatSettings.LongTimeFormat then if s = StripAMPM(FWorkbook.FormatSettings.LongTimeFormat) then
nf := nfLongTime nf := IfThen(isAMPM, nfLongTimeAM, nfLongTime)
else else
if s = FWorkbook.FormatSettings.ShortTimeFormat then if s = StripAMPM(FWorkbook.FormatSettings.ShortTimeFormat) then
nf := nfShortTime nf := IfThen(isAMPM, nfShortTimeAM, nfShortTime)
else else
nf := nfFmtDateTime; nf := nfFmtDateTime;
FSections[FCurrSection].NumFormat := nf;
end;
except FSections[FCurrSection].NumFormat := nf;
FStatus := psErrNoValidDateTimeFormat;
end; end;
end; end;
@ -470,17 +581,23 @@ begin
token := FCurrent^; token := FCurrent^;
case token of case token of
// Strip Excel's formatting symbols // Strip Excel's formatting symbols
'\', '*' : ; '\', '*':
'_' : inc(FCurrent); ;
'"' : begin '_':
inc(FCurrent);
'"':
begin
inc(FCurrent); inc(FCurrent);
ScanText; ScanText;
end; end;
'0', '#', '.', ',', '-': ScanNumber; '0', '#', '.', ',', '-':
'y', 'Y', 'm', 'M', ScanNumber;
'd', 'D', 'h', 's', '[': ScanDateTime; 'y', 'Y', 'm', 'M', 'd', 'D', 'h', 'N', 'n', 's', '[':
' ' : AddChar(token); ScanDateTime;
';' : begin ' ':
AddChar(token);
';':
begin
done := true; done := true;
dec(FCurrent); dec(FCurrent);
// Cursor must stay on the ";" // Cursor must stay on the ";"

View File

@ -184,7 +184,7 @@ end;
procedure TsSpreadOpenDocReader.CreateNumFormatList; procedure TsSpreadOpenDocReader.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsSpreadOpenDocNumFormatList.Create; FNumFormatList := TsSpreadOpenDocNumFormatList.Create(Workbook);
end; end;
function TsSpreadOpenDocReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string; function TsSpreadOpenDocReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string;
@ -471,7 +471,7 @@ end;
procedure TsSpreadOpenDocWriter.CreateNumFormatList; procedure TsSpreadOpenDocWriter.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsSpreadOpenDocNumFormatList.Create; FNumFormatList := TsSpreadOpenDocNumFormatList.Create(Workbook);
end; end;
procedure TsSpreadOpenDocWriter.WriteMimetype; procedure TsSpreadOpenDocWriter.WriteMimetype;

View File

@ -362,16 +362,21 @@ type
FOnChangeCell: TsCellEvent; FOnChangeCell: TsCellEvent;
FOnChangeFont: TsCellEvent; FOnChangeFont: TsCellEvent;
procedure RemoveCallback(data, arg: pointer); procedure RemoveCallback(data, arg: pointer);
protected protected
procedure ChangedCell(ARow, ACol: Cardinal); procedure ChangedCell(ARow, ACol: Cardinal);
procedure ChangedFont(ARow, ACol: Cardinal); procedure ChangedFont(ARow, ACol: Cardinal);
public public
Name: string; Name: string;
{ Base methods } { Base methods }
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
{ Utils } { Utils }
class function CellPosToText(ARow, ACol: Cardinal): string; class function CellPosToText(ARow, ACol: Cardinal): string;
{ Data manipulation methods - For Cells } { Data manipulation methods - For Cells }
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet); procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal);
@ -390,35 +395,25 @@ type
function ReadUsedFormatting(ARow, ACol: Cardinal): TsUsedFormattingFields; function ReadUsedFormatting(ARow, ACol: Cardinal): TsUsedFormattingFields;
function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor; function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor;
procedure RemoveAllCells; procedure RemoveAllCells;
{ Writing of values } { Writing of values }
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteBlank(ARow, ACol: Cardinal);
procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean);
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue);
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2; AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2;
ACurrencySymbol: String = ''); overload; ACurrencySymbol: String = ''); overload;
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormatString: String); overload; AFormatString: String); overload;
procedure WriteBlank(ARow, ACol: Cardinal);
procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean);
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
procedure WriteDecimals(ARow, ACol: Cardinal; ADecimals: byte); overload;
procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload;
procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TErrorValue);
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula);
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring);
{ Writing of cell attributes } { Writing of cell attributes }
procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat;
const AFormatString: String = '');
function WriteFont(ARow, ACol: Cardinal; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload;
procedure WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer); overload;
function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer;
function WriteFontName(ARow, ACol: Cardinal; AFontName: String): Integer;
function WriteFontSize(ARow, ACol: Cardinal; ASize: Single): Integer;
function WriteFontStyle(ARow, ACol: Cardinal; AStyle: TsFontStyles): Integer;
procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation);
procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields);
procedure WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor); procedure WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor);
procedure WriteBorderColor(ARow, ACol: Cardinal; ABorder: TsCellBorder; AColor: TsColor); procedure WriteBorderColor(ARow, ACol: Cardinal; ABorder: TsCellBorder; AColor: TsColor);
procedure WriteBorderLineStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; procedure WriteBorderLineStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder;
ALineStyle: TsLineStyle); ALineStyle: TsLineStyle);
@ -428,9 +423,31 @@ type
procedure WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; procedure WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder;
ALineStyle: TsLineStyle; AColor: TsColor); overload; ALineStyle: TsLineStyle; AColor: TsColor); overload;
procedure WriteBorderStyles(ARow, ACol: Cardinal; const AStyles: TsCellBorderStyles); procedure WriteBorderStyles(ARow, ACol: Cardinal; const AStyles: TsCellBorderStyles);
procedure WriteDecimals(ARow, ACol: Cardinal; ADecimals: byte); overload;
procedure WriteDecimals(ACell: PCell; ADecimals: Byte); overload;
function WriteFont(ARow, ACol: Cardinal; const AFontName: String;
AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload;
procedure WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer); overload;
function WriteFontColor(ARow, ACol: Cardinal; AFontColor: TsColor): Integer;
function WriteFontName(ARow, ACol: Cardinal; AFontName: String): Integer;
function WriteFontSize(ARow, ACol: Cardinal; ASize: Single): Integer;
function WriteFontStyle(ARow, ACol: Cardinal; AStyle: TsFontStyles): Integer;
procedure WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment); procedure WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment);
procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat;
const AFormatString: String = '');
procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation);
procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields);
procedure WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment); procedure WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment);
procedure WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean); procedure WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean);
{ Data manipulation methods - For Rows and Cols } { Data manipulation methods - For Rows and Cols }
function FindRow(ARow: Cardinal): PRow; function FindRow(ARow: Cardinal): PRow;
function FindCol(ACol: Cardinal): PCol; function FindCol(ACol: Cardinal): PCol;
@ -442,11 +459,13 @@ type
procedure WriteRowHeight(ARow: Cardinal; AHeight: Single); procedure WriteRowHeight(ARow: Cardinal; AHeight: Single);
procedure WriteColInfo(ACol: Cardinal; AData: TCol); procedure WriteColInfo(ACol: Cardinal; AData: TCol);
procedure WriteColWidth(ACol: Cardinal; AWidth: Single); procedure WriteColWidth(ACol: Cardinal; AWidth: Single);
{ Properties } { Properties }
property Cells: TAVLTree read FCells; property Cells: TAVLTree read FCells;
property Cols: TIndexedAVLTree read FCols; property Cols: TIndexedAVLTree read FCols;
property Rows: TIndexedAVLTree read FRows; property Rows: TIndexedAVLTree read FRows;
property Workbook: TsWorkbook read FWorkbook; property Workbook: TsWorkbook read FWorkbook;
// These are properties to interface to fpspreadsheetgrid. // These are properties to interface to fpspreadsheetgrid.
property Options: TsSheetOptions read FOptions write FOptions; property Options: TsSheetOptions read FOptions write FOptions;
property LeftPaneWidth: Integer read FLeftPaneWidth write FLeftPaneWidth; property LeftPaneWidth: Integer read FLeftPaneWidth write FLeftPaneWidth;
@ -547,7 +566,7 @@ type
var ACurrencySymbol: String); virtual; var ACurrencySymbol: String); virtual;
procedure RemoveFormat(AIndex: Integer); procedure RemoveFormat(AIndex: Integer);
public public
constructor Create; constructor Create(AWorkbook: TsWorkbook);
destructor Destroy; override; destructor Destroy; override;
function AddFormat(AFormatCell: PCell): Integer; overload; function AddFormat(AFormatCell: PCell): Integer; overload;
function AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat; function AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat;
@ -718,7 +737,7 @@ procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer);
implementation implementation
uses uses
Math, StrUtils, fpsutils; Math, StrUtils, fpsUtils, fpsNumFormatParser;
{ Translatable strings } { Translatable strings }
resourcestring resourcestring
@ -727,6 +746,9 @@ resourcestring
lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file'; lpNoValidSpreadsheetFile = '"%s" is not a valid spreadsheet file';
lpUnknownSpreadsheetFormat = 'unknown format'; lpUnknownSpreadsheetFormat = 'unknown format';
lpInvalidFontIndex = 'Invalid font index'; lpInvalidFontIndex = 'Invalid font index';
lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
lpNoValidNumberFormatString = 'No valid number format string.';
lpNoValidDateTimeFormatString = 'No valid date/time format string.';
lpTRUE = 'TRUE'; lpTRUE = 'TRUE';
lpFALSE = 'FALSE'; lpFALSE = 'FALSE';
lpErrEmptyIntersection = '#NULL!'; lpErrEmptyIntersection = '#NULL!';
@ -1407,14 +1429,19 @@ begin
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber; ACell^.NumberValue := ANumber;
ACell^.Decimals := ADecimals; ACell^.Decimals := ADecimals;
if IsDateTimeFormat(AFormat) then
raise Exception.Create(lpInvalidNumberFormat);
if AFormat <> nfGeneral then begin if AFormat <> nfGeneral then begin
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := AFormat; ACell^.NumberFormat := AFormat;
ACell^.Decimals := ADecimals; ACell^.Decimals := ADecimals;
ACell^.CurrencySymbol := ACurrencySymbol; ACell^.CurrencySymbol := ACurrencySymbol;
ACell^.NumberFormatStr := BuildNumFormatString(ACell^.NumberFormat, ACell^.NumberFormatStr := BuildNumberFormatString(ACell^.NumberFormat,
Workbook.FormatSettings, ADecimals, ACurrencySymbol); Workbook.FormatSettings, ADecimals, ACurrencySymbol);
end; end;
ChangedCell(ARow, ACol); ChangedCell(ARow, ACol);
end; end;
@ -1427,13 +1454,32 @@ procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: Double;
AFormatString: String); AFormatString: String);
var var
ACell: PCell; ACell: PCell;
parser: TsNumFormatParser;
nf: TsNumberFormat;
begin begin
parser := TsNumFormatParser.Create(Workbook, AFormatString, cdToFPSpreadsheet);
try
// Format string ok?
if parser.Status <> psOK then
raise Exception.Create(lpNoValidNumberFormatString);
if IsDateTimeFormat(parser.Builtin_NumFormat)
then raise Exception.Create(lpInvalidNumberFormat);
// If format string matches a built-in format use its format identifier,
// All this is considered when calling Builtin_NumFormat of the parser.
nf := parser.Builtin_NumFormat;
finally
parser.Free;
end;
ACell := GetCell(ARow, ACol); ACell := GetCell(ARow, ACol);
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber; ACell^.NumberValue := ANumber;
ACell^.NumberFormat := nfCustom; ACell^.NumberFormat := nf;
ACell^.NumberFormatStr := AFormatString; ACell^.NumberFormatStr := AFormatString;
ACell^.Decimals := 0;
ACell^.CurrencySymbol := '';
ChangedCell(ARow, ACol); ChangedCell(ARow, ACol);
end; end;
@ -1482,6 +1528,7 @@ end;
Must follow the rules for "FormatDateTime", or use Must follow the rules for "FormatDateTime", or use
"dm" as abbreviation for "d/mmm", "my" for "mmm/yy", "dm" as abbreviation for "d/mmm", "my" for "mmm/yy",
"ms" for "nn:ss", "msz" for "nn:ss.z" (optional) "ms" for "nn:ss", "msz" for "nn:ss.z" (optional)
or use any other free format (at your own risk...)
Note: at least Excel xls does not recognize a separate datetime cell type: Note: at least Excel xls does not recognize a separate datetime cell type:
a datetime is stored as a (floating point) Number, and the cell is formatted a datetime is stored as a (floating point) Number, and the cell is formatted
@ -1494,9 +1541,25 @@ procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
var var
ACell: PCell; ACell: PCell;
fmt: String; fmt: String;
parser: TsNumFormatParser;
begin begin
ACell := GetCell(ARow, ACol); if AFormat = nfFmtDateTime then begin
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
parser := TsNumFormatParser.Create(Workbook, AFormatStr, cdToFPSpreadsheet);
try
// Check that the format string can be reckognized.
if parser.Status <> psOK then
raise Exception.Create(lpNoValidNumberFormatString);
// Check that the format string belongs to date/time values
if not (IsDateTimeFormat(parser.Builtin_NumFormat) or (parser.Builtin_NumFormat = nfFmtDateTime))
then raise Exception.Create(lpNoValidDateTimeFormatString);
AFormatStr := parser.FormatString;
finally
parser.Free;
end;
end;
ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctDateTime; ACell^.ContentType := cctDateTime;
ACell^.DateTimeValue := AValue; ACell^.DateTimeValue := AValue;
// Date/time is actually a number field in Excel. // Date/time is actually a number field in Excel.
@ -1504,34 +1567,8 @@ begin
// The user can choose another date format if he wants to // The user can choose another date format if he wants to
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := AFormat; ACell^.NumberFormat := AFormat;
case AFormat of ACell^.NumberFormatStr := AFormatStr;
nfShortDateTime:
ACell^.NumberFormatStr := FormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat;
nfShortDate:
ACell^.NumberFormatStr := FormatSettings.ShortDateFormat;
nfLongDate:
ACell^.NumberFormatStr := 'dd/mmm/yyyy';
nfShortTime:
ACell^.NumberFormatStr := 't';
nfLongTime:
ACell^.NumberFormatStr := 'tt';
nfShortTimeAM:
ACell^.NumberFormatStr := 'hh:nn AM/PM';
nfLongTimeAM:
ACell^.NumberFormatStr := 'hh:nn:ss AM/PM';
nfFmtDateTime:
begin
fmt := lowercase(AFormatStr);
if fmt = 'dm' then ACell^.NumberFormatStr := 'd/mmm'
else if fmt = 'my' then ACell^.NumberFormatStr := 'mmm/yy'
else if fmt = 'ms' then ACell^.NumberFormatStr := 'nn:ss'
else if fmt = 'msz' then ACell^.NumberFormatStr := 'nn:ss.z'
else ACell^.NumberFormatStr := AFormatStr;
end;
nfTimeInterval:
if AFormatStr = '' then ACell^.NumberFormatStr := '[h]:nn:ss'
else ACell^.NumberFormatStr := AFormatStr;
end;
ChangedCell(ARow, ACol); ChangedCell(ARow, ACol);
end; end;
@ -1544,7 +1581,7 @@ procedure TsWorksheet.WriteDecimals(ACell: PCell; ADecimals: Byte);
begin begin
if (ACell <> nil) and (ACell^.ContentType = cctNumber) then begin if (ACell <> nil) and (ACell^.ContentType = cctNumber) then begin
ACell^.Decimals := ADecimals; ACell^.Decimals := ADecimals;
ACell^.NumberFormatStr := BuildNumFormatString(ACell^.NumberFormat, ACell^.NumberFormatStr := BuildNumberFormatString(ACell^.NumberFormat,
FWorkbook.FormatSettings, ADecimals, ACell^.CurrencySymbol); FWorkbook.FormatSettings, ADecimals, ACell^.CurrencySymbol);
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
end; end;
@ -1603,7 +1640,7 @@ begin
Include(ACell^.UsedFormattingFields, uffNumberFormat); Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := ANumberFormat; ACell^.NumberFormat := ANumberFormat;
if (AFormatString = '') then if (AFormatString = '') then
ACell^.NumberFormatStr := BuildNumFormatString(ANumberFormat, ACell^.NumberFormatStr := BuildNumberFormatString(ANumberFormat,
Workbook.FormatSettings, ACell^.Decimals, ACell^.CurrencySymbol) Workbook.FormatSettings, ACell^.Decimals, ACell^.CurrencySymbol)
else else
ACell^.NumberFormatStr := AFormatString; ACell^.NumberFormatStr := AFormatString;
@ -2577,9 +2614,10 @@ end;
{ TsCustomNumFormatList } { TsCustomNumFormatList }
constructor TsCustomNumFormatList.Create; constructor TsCustomNumFormatList.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create; inherited Create;
FWorkbook := AWorkbook;
AddBuiltinFormats; AddBuiltinFormats;
end; end;
@ -2599,6 +2637,13 @@ begin
item := TsNumFormatData.Create; item := TsNumFormatData.Create;
item.Index := AFormatIndex; item.Index := AFormatIndex;
item.NumFormat := ANumFormat; item.NumFormat := ANumFormat;
if IsDateTimeFormat(ANumFormat) then
AFormatString := BuildDateTimeFormatString(ANumFormat, Workbook.FormatSettings,
AFormatString)
else
if item.NumFormat <> nfCustom then
AFormatString := BuildNumberFormatString(ANumFormat, Workbook.FormatSettings,
ADecimals, ACurrencySymbol);
item.FormatString := AFormatString; item.FormatString := AFormatString;
item.Decimals := ADecimals; item.Decimals := ADecimals;
item.CurrencySymbol := ACurrencySymbol; item.CurrencySymbol := ACurrencySymbol;
@ -2644,6 +2689,38 @@ end;
If the format string cannot be directly handled by fpc it has to be transformed If the format string cannot be directly handled by fpc it has to be transformed
to make it compatible. Can be done in overridden versions which know more to make it compatible. Can be done in overridden versions which know more
about the structure of the string in the actual file format. } about the structure of the string in the actual file format. }
procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer;
var AFormatString: String; var ANumFormat: TsNumberFormat;
var ADecimals: Byte; var ACurrencySymbol: String);
var
parser: TsNumFormatParser;
fmt: String;
lFormatData: TsNumFormatData;
i: Integer;
begin
i := Find(AFormatIndex);
if i > 0 then begin
lFormatData := Items[i];
fmt := lFormatData.FormatString;
end else
fmt := AFormatString;
parser := TsNumFormatParser.Create(Workbook, fmt, cdToFPSpreadsheet);
try
if parser.Status = psOK then begin
ANumFormat := parser.Builtin_NumFormat;
AFormatString := parser.FormatString;
if not (parser.Builtin_NumFormat in [nfCustom, nfFmtDateTime]) then begin
ADecimals := parser.ParsedSections[0].Decimals;
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
end;
end;
finally
parser.Free;
end;
end;
(*
procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer; procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer;
var AFormatString: String; var ANumFormat: TsNumberFormat; var AFormatString: String; var ANumFormat: TsNumberFormat;
var ADecimals: Byte; var ACurrencySymbol: String); var ADecimals: Byte; var ACurrencySymbol: String);
@ -2749,6 +2826,7 @@ begin
ANumFormat := nfCustom; ANumFormat := nfCustom;
end; end;
end; end;
*)
{ Called from the reader when a format item has been read from the file. { Called from the reader when a format item has been read from the file.
Determines the numFormat type, format string etc and stores the format in the Determines the numFormat type, format string etc and stores the format in the
@ -2816,8 +2894,8 @@ begin
item := Items[Result]; item := Items[Result];
if (item <> nil) and (item.NumFormat = ANumFormat) then if (item <> nil) and (item.NumFormat = ANumFormat) then
exit; exit;
end end;
else
if (ANumFormat = nfFmtDateTime) then begin if (ANumFormat = nfFmtDateTime) then begin
fmt := lowercase(AFormatString); fmt := lowercase(AFormatString);
for Result := 0 to Count-1 do begin for Result := 0 to Count-1 do begin
@ -2847,9 +2925,10 @@ begin
if fmt = lowercase(item.FormatString) then if fmt = lowercase(item.FormatString) then
exit; exit;
end; end;
end else end;
// Check only the format string for nfCustom. // Check only the format string for nfCustom.
if (ANumFormat = nfCustom) then begin if (ANumFormat = nfCustom) then
for Result := 0 to Count-1 do begin for Result := 0 to Count-1 do begin
item := Items[Result]; item := Items[Result];
if (item <> nil) if (item <> nil)
@ -2858,7 +2937,7 @@ begin
then then
exit; exit;
end; end;
end else
// The other formats can carry additional information // The other formats can carry additional information
for Result := 0 to Count-1 do begin for Result := 0 to Count-1 do begin
item := Items[Result]; item := Items[Result];
@ -3099,7 +3178,8 @@ var
decs: Byte; decs: Byte;
begin begin
if ACell^.NumberFormat = nfFmtDateTime then begin if ACell^.NumberFormat = nfFmtDateTime then begin
if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then decs := CountDecs(ACell^.NumberFormatStr, ['0', 'z', 'Z']);
// if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then
ACell^.Decimals := decs; ACell^.Decimals := decs;
end; end;
end; end;

View File

@ -17,6 +17,7 @@ uses
// Exported types // Exported types
type type
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection); TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
TsDecsChars = set of char;
const const
// Date formatting string for unambiguous date/time display as strings // Date formatting string for unambiguous date/time display as strings
@ -60,19 +61,29 @@ function UTF8TextToXMLText(AText: ansistring): ansistring;
function TwipsToMillimeters(AValue: Integer): Single; function TwipsToMillimeters(AValue: Integer): Single;
function MillimetersToTwips(AValue: Single): Integer; function MillimetersToTwips(AValue: Single): Integer;
function IfThen(ACondition: Boolean; AValue1,AValue2: TsNumberFormat): TsNumberFormat; overload;
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean;
(*
function IsCurrencyFormat(s: String; out Decimals: Byte; out CurrSymbol: String; function IsCurrencyFormat(s: String; out Decimals: Byte; out CurrSymbol: String;
out IsCurrencyRedFmt, IsCurrencyDashFmt: Boolean): Boolean; out IsCurrencyRedFmt, IsCurrencyDashFmt: Boolean): Boolean;
function IsExpNumberFormat(s: String; out Decimals: Byte; out IsSci: Boolean): Boolean; function IsExpNumberFormat(s: String; out Decimals: Byte; out IsSci: Boolean): Boolean;
function IsFixedNumberFormat(s: String; out Decimals: Byte): Boolean; function IsFixedNumberFormat(s: String; out Decimals: Byte): Boolean;
function IsPercentNumberFormat(s: String; out Decimals: Byte): Boolean; function IsPercentNumberFormat(s: String; out Decimals: Byte): Boolean;
function IsThousandSepNumberFormat(s: String; out Decimals: Byte): Boolean; function IsThousandSepNumberFormat(s: String; out Decimals: Byte): Boolean;
function IsDateFormat(s: String; out IsLong: Boolean): Boolean;
function IsTimeFormat(s: String; out isLong, isAMPM, isInterval: Boolean;
out SecDecimals: Byte): Boolean;
function BuildNumFormatString(ANumberFormat: TsNumberFormat; function IsDateFormat(s: String; out IsLong: Boolean): Boolean;
{function IsTimeFormat(s: String; out isLong, isAMPM, isInterval: Boolean;
out SecDecimals: Byte): Boolean;
*)
function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; const AFormatSettings: TFormatSettings; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'): String; ACurrencySymbol: String = '?'): String;
function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; AFormatString: String = ''): String;
function StripAMPM(const ATimeFormatString: String): String;
function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte;
function SciFloat(AValue: Double; ADecimals: Byte): String; function SciFloat(AValue: Double; ADecimals: Byte): String;
//function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String; //function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String;
@ -494,8 +505,21 @@ begin
end; end;
{ Returns either AValue1 or AValue2, depending on the condition.
For reduciton of typing... }
function IfThen(ACondition: Boolean; AValue1, AValue2: TsNumberFormat): TsNumberFormat;
begin
if ACondition then Result := AValue1 else Result := AValue2;
end;
{ Format checking procedures } { Format checking procedures }
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean;
begin
Result := AFormat in [nfFmtDateTime, nfShortDateTime, nfShortDate, nfLongDate,
nfShortTime. nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval];
end;
(*
{ This simple parsing procedure of the Excel format string checks for a fixed { This simple parsing procedure of the Excel format string checks for a fixed
float format s, i.e. s can be '0', '0.00', '000', '0,000', and returns the float format s, i.e. s can be '0', '0.00', '000', '0,000', and returns the
number of decimals, i.e. number of zeros behind the decimal point } number of decimals, i.e. number of zeros behind the decimal point }
@ -661,7 +685,7 @@ begin
if ph > 0 then IsSci := true; if ph > 0 then IsSci := true;
end; end;
end; end;
*)
{ IsDateFormat checks if the format string s corresponds to a date format } { IsDateFormat checks if the format string s corresponds to a date format }
function IsDateFormat(s: String; out IsLong: Boolean): Boolean; function IsDateFormat(s: String; out IsLong: Boolean): Boolean;
begin begin
@ -744,9 +768,58 @@ begin
end; end;
end; end;
{ Builds a number format string from the numberformat code and the count of { Builds a date/time format string from the numberformat code. If the format code
decimals. } is nfFmtDateTime the given AFormatString is used. AFormatString can use the
function BuildNumFormatString(ANumberFormat: TsNumberFormat; abbreviations "dm" (for "d/mmm"), "my" (for "mmm/yy"), "ms" (for "mm:ss")
and "msz" (for "mm:ss.z"). }
function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; AFormatString: String = '') : string;
var
fmt: String;
begin
case ANumberFormat of
nfFmtDateTime:
begin
fmt := lowercase(AFormatString);
if (fmt = 'dm') then Result := 'd/mmm'
else if (fmt = 'my') then Result := 'mmm/yy'
else if (fmt = 'ms') then Result := 'nn:ss'
else if (fmt = 'msz') then Result := 'nn:ss.z'
else Result := AFormatString;
end;
nfShortDateTime:
Result := AFormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat;
nfShortDate:
Result := AFormatSettings.ShortDateFormat;
nfLongDate:
Result := AFormatSettings.LongDateFormat;
nfShortTime:
Result := StripAMPM(AFormatSettings.ShortTimeFormat);
nfLongTime:
Result := StripAMPM(AFormatSettings.LongTimeFormat);
nfShortTimeAM:
begin
Result := AFormatSettings.ShortTimeFormat;
if pos('a', lowercase(AFormatSettings.ShortTimeFormat)) = 0 then
Result := Format('%s %s/%s', [Result, AFormatSettings.TimeAMString, AFormatSettings.TimePMString]);
end;
nfLongTimeAM:
begin
Result := AFormatSettings.LongTimeFormat;
if pos('a', lowercase(AFormatSettings.LongTimeFormat)) = 0 then
Result := Format('%s %s/%s', [Result, AFormatSettings.TimeAMString, AFormatSettings.TimePMString]);
end;
nfTimeInterval:
if AFormatString = '' then
Result := '[h]:mm:ss'
else
Result := AFormatString;
end;
end;
{ Builds a number format string from the numberformat code, the count of
decimals, and the currencysymbol (if not empty). }
function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1; const AFormatSettings: TFormatSettings; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'): String; ACurrencySymbol: String = '?'): String;
const const
@ -778,6 +851,7 @@ var
decs: String; decs: String;
cf, ncf: Byte; cf, ncf: Byte;
begin begin
Result := '';
cf := AFormatSettings.CurrencyFormat; cf := AFormatSettings.CurrencyFormat;
ncf := AFormatSettings.NegCurrFormat; ncf := AFormatSettings.NegCurrFormat;
if ADecimals = -1 then ADecimals := AFormatSettings.CurrencyDecimals; if ADecimals = -1 then ADecimals := AFormatSettings.CurrencyDecimals;
@ -824,6 +898,37 @@ begin
end; end;
end; end;
function StripAMPM(const ATimeFormatString: String): String;
var
i: Integer;
begin
Result := '';
i := 1;
while i <= Length(ATimeFormatString) do begin
if ATimeFormatString[i] in ['a', 'A'] then begin
inc(i);
while (i <= Length(ATimeFormatString)) and (ATimeFormatString[i] in ['p', 'P', 'm', 'M', '/']) do
inc(i);
end else
Result := Result + ATimeFormatString[i];
inc(i);
end;
end;
function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte;
var
i: Integer;
begin
Result := 0;
for i:=Length(AFormatString) downto 1 do begin
if AFormatString[i] in ADecChars then inc(Result);
if AFormatString[i] = '.' then exit;
end;
// Comes to this point when there is no decimal separtor.
Result := 0;
end;
{ Formats the number AValue in "scientific" format with the given number of { Formats the number AValue in "scientific" format with the given number of
decimals. "Scientific" is the same as "exponential", but with exponents rounded decimals. "Scientific" is the same as "exponential", but with exponents rounded
to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). } to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). }

View File

@ -352,7 +352,7 @@ end;
procedure TsWikiTableWriter.CreateNumFormatList; procedure TsWikiTableWriter.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsWikiTableNumFormatList.Create; FNumFormatList := TsWikiTableNumFormatList.Create(Workbook);
end; end;
procedure TsWikiTableWriter.WriteToStrings(AStrings: TStrings); procedure TsWikiTableWriter.WriteToStrings(AStrings: TStrings);

View File

@ -43,7 +43,7 @@ type
protected protected
procedure AddBuiltinFormats; override; procedure AddBuiltinFormats; override;
public public
constructor Create; constructor Create(AWorkbook: TsWorkbook);
function FormatStringForWriting(AIndex: Integer): String; override; function FormatStringForWriting(AIndex: Integer): String; override;
end; end;
@ -166,9 +166,9 @@ const
{ TsBIFF2NumFormatList } { TsBIFF2NumFormatList }
constructor TsBIFF2NumFormatList.Create; constructor TsBIFF2NumFormatList.Create(AWorkbook: TsWorkbook);
begin begin
inherited Create; inherited Create(AWorkbook);
end; end;
procedure TsBIFF2NumFormatList.AddBuiltinFormats; procedure TsBIFF2NumFormatList.AddBuiltinFormats;
@ -283,7 +283,7 @@ end;
procedure TsSpreadBIFF2Reader.CreateNumFormatList; procedure TsSpreadBIFF2Reader.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFF2NumFormatList.Create; FNumFormatList := TsBIFF2NumFormatList.Create(Workbook);
end; end;
{ Extracts the number format data from an XF record indexed by AXFIndex. { Extracts the number format data from an XF record indexed by AXFIndex.
@ -701,7 +701,7 @@ end;
procedure TsSpreadBIFF2Writer.CreateNumFormatList; procedure TsSpreadBIFF2Writer.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFF2NumFormatList.Create; FNumFormatList := TsBIFF2NumFormatList.Create(Workbook);
end; end;
function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word; function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word;

View File

@ -593,13 +593,15 @@ procedure TsBIFFNumFormatList.Analyze(AFormatIndex: Integer;
var AFormatString: String; var ANumFormat: TsNumberFormat; var AFormatString: String; var ANumFormat: TsNumberFormat;
var ADecimals: Byte; var ACurrencySymbol: String); var ADecimals: Byte; var ACurrencySymbol: String);
var var
parser: TsNumFormatParser;
fmt: String; fmt: String;
{
parser: TsNumFormatParser;
sections: TsNumFormatSections;
}
begin begin
{ (*
AFormatString := 'hh:mm AM/PM'; //"€" #,##.0;[red]"$" -#,##.000;-'; AFormatString := 'hh:mm:ss.0 AM/PM'; //"€" #,##.0;[red]"$" -#,##.000;-';
parser := TsNumFormatParser.Create(Workbook, AFormatString); parser := TsNumFormatParser.Create(Workbook, AFormatString);
try try
@ -607,10 +609,18 @@ begin
ANumFormat := parser.ParsedSections[0].NumFormat; ANumFormat := parser.ParsedSections[0].NumFormat;
ADecimals := parser.ParsedSections[0].Decimals; ADecimals := parser.ParsedSections[0].Decimals;
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol; ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
parser.CopySectionsTo(sections);
finally finally
parser.Free; parser.Free;
end; end;
}
parser := TsNumFormatParser.Create(Workbook, sections);
try
fmt := parser.FormatString;
finally
parser.Free;
end;
*)
fmt := Lowercase(AFormatString); fmt := Lowercase(AFormatString);
{ Check the built-in formats first: { Check the built-in formats first:
@ -660,6 +670,18 @@ function TsBIFFNumFormatList.FormatStringForWriting(AIndex: Integer): String;
var var
item: TsNumFormatData; item: TsNumFormatData;
i: Integer; i: Integer;
procedure FixN(var s: String);
// The minutes in short time formats are coded by "n" in fpc, but Excel wants "m".
var
i: Integer;
begin
for i:=1 to Length(s) do
case s[i] of
'n', 'N': s[i] := 'm'; // no "M" which will be interpreted as "Month"
end;
end;
begin begin
Result := inherited FormatStringForWriting(AIndex); Result := inherited FormatStringForWriting(AIndex);
item := Items[AIndex]; item := Items[AIndex];
@ -670,15 +692,19 @@ begin
for i:=1 to Length(Result) do begin for i:=1 to Length(Result) do begin
// The milliseccond format contains the symbol "z" in fpc, but Excel wants "0" // The milliseccond format contains the symbol "z" in fpc, but Excel wants "0"
if Result[i] in ['z', 'Z'] then Result[i] := '0'; if Result[i] in ['z', 'Z'] then Result[i] := '0';
// The minutes in short time formats are coded by "n" in fpc, but Excel wants "m".
if Result[i] in ['n', 'N'] then Result[i] := 'm';
end; end;
FixN(Result);
end; end;
nfTimeInterval: nfTimeInterval:
begin
// Time interval format string could still be without square brackets // Time interval format string could still be without square brackets
// if added by user. // if added by user.
// We check here for safety and add the brackets if not there. // We check here for safety and add the brackets if not there.
MakeTimeIntervalMask(item.FormatString, Result); MakeTimeIntervalMask(item.FormatString, Result);
FixN(Result);
end;
nfShortTime, nfShortTimeAM, nfLongTime, nfLongTimeAM:
FixN(Result);
nfCurrencyRed, nfCurrencyDashRed: nfCurrencyRed, nfCurrencyDashRed:
begin begin
i := Pos(';', item.FormatString); i := Pos(';', item.FormatString);
@ -765,7 +791,7 @@ end;
procedure TsSpreadBIFFReader.CreateNumFormatList; procedure TsSpreadBIFFReader.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFFNumFormatList.Create; FNumFormatList := TsBIFFNumFormatList.Create(Workbook);
end; end;
{ Extracts a number out of an RK value. { Extracts a number out of an RK value.
@ -812,9 +838,11 @@ procedure TsSpreadBIFFReader.ExtractNumberFormat(AXFIndex: WORD;
decs: Byte; decs: Byte;
i: Integer; i: Integer;
begin begin
if IsTimeFormat(ANumberFormatStr, isLong, isAMPM, isInterval, decs) decs := CountDecs(ANumberFormatStr, ['0', 'z', 'Z']);
{ if IsTimeFormat(ANumberFormatStr, isLong, isAMPM, isInterval, decs)
and (decs > 0) and (decs > 0)
then then }
if decs > 0 then
for i:= Length(ANumberFormatStr) downto 1 do for i:= Length(ANumberFormatStr) downto 1 do
case ANumberFormatStr[i] of case ANumberFormatStr[i] of
'0': ANumberFormatStr[i] := 'z'; '0': ANumberFormatStr[i] := 'z';
@ -1361,7 +1389,7 @@ end;
procedure TsSpreadBIFFWriter.CreateNumFormatList; procedure TsSpreadBIFFWriter.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFFNumFormatList.Create; FNumFormatList := TsBIFFNumFormatList.Create(Workbook);
end; end;
function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID( function TsSpreadBIFFWriter.FormulaElementKindToExcelTokenID(
@ -1679,19 +1707,16 @@ end;
procedure TsSpreadBIFFWriter.WriteFormats(AStream: TStream); procedure TsSpreadBIFFWriter.WriteFormats(AStream: TStream);
var var
i: Integer; i: Integer;
item: TsNumFormatData; item: TsNumFormatData;
begin begin
ListAllNumFormats; ListAllNumFormats;
item := NumFormatList[20];
i := NumFormatList.Find(NumFormatList.FirstFormatIndexInFile); i := NumFormatList.Find(NumFormatList.FirstFormatIndexInFile);
if i > -1 then if i > -1 then
while i < NumFormatList.Count do begin while i < NumFormatList.Count do begin
if NumFormatList[i] <> nil then item := NumFormatList[i];
WriteFormat(AStream, NumFormatList[i], i); if item <> nil then begin
WriteFormat(AStream, item, i);
end;
inc(i); inc(i);
end; end;
end; end;

View File

@ -393,7 +393,7 @@ end;
procedure TsSpreadOOXMLWriter.CreateNumFormatList; procedure TsSpreadOOXMLWriter.CreateNumFormatList;
begin begin
FreeAndNil(FNumFormatList); FreeAndNil(FNumFormatList);
FNumFormatList := TsOOXMLNumFormatList.Create; FNumFormatList := TsOOXMLNumFormatList.Create(Workbook);
end; end;
{ {