fpspreadsheet: Major reconstruction of numberformat parser to facilitate creation of xml formats for ods. Some minor regressions in unit tests and fpsgrid demo to be fixed. Removed elements "Decimals" and "CurrencySymbol" from TCell (this information is taken from the format string now). Removed the built-in format nfFmtDateTime (makes life easier, use nfCustom instead).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3156 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-12 22:20:45 +00:00
parent 885faa4ed6
commit a4ee00f870
15 changed files with 2035 additions and 1151 deletions

View File

@ -64,9 +64,6 @@
</Debugging>
</Linking>
<Other>
<CompilerMessages>
<UseMsgFile Value="True"/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>

View File

@ -53,9 +53,9 @@ begin
{ non-frozen panes not working, at the moment. Requires SELECTION records?
MyWorksheet.LeftPaneWidth := 20*72*2; // 72 pt = inch --> 2 inches = 5 cm
}
// Write some cells
MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1
MyWorksheet.WriteNumber(0, 0, 0.0, nfSci, 1);
(*
MyWorksheet.WriteNumber(0, 1, 2.0);// B1
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
MyWorksheet.WriteNumber(0, 3, 4.0);// D1
@ -131,7 +131,7 @@ begin
MyWorksheet.WriteUTF8Text(8, 3, 'Colors...');
MyWorksheet.WriteFont(8, 3, 'Courier New', 12, [fssUnderline], scBlue);
MyWorksheet.WriteBackgroundColor(8, 3, scYellow);
{}
{
// Uncomment this to test large XLS files
for i := 50 to 1000 do
@ -174,6 +174,9 @@ begin
MyWorksheet.WriteUTF8Text(r, 0, 'Writing current date/time:');
inc(r, 2);
// Write current date/time to cells B11:B16
MyWorksheet.WriteUTF8Text(r, 0, '(default format)');
MyWorksheet.WriteDateTime(r, 1, now);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDate');
MyWorksheet.WriteDateTime(r, 1, now, nfShortDate);
inc(r);
@ -189,11 +192,11 @@ begin
MyWorksheet.WriteUTF8Text(r, 0, 'nfShortDateTime');
MyWorksheet.WriteDateTime(r, 1, now, nfShortDateTime);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, DM');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'DM');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, ''dd/mmm''');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'dd/mmm');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MY');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MY');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, ''MMM/YY''');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'mmm/yy');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfShortTimeAM');
MyWorksheet.WriteDateTime(r, 1, now, nfShortTimeAM);
@ -201,14 +204,14 @@ begin
MyWorksheet.WriteUTF8Text(r, 0, 'nfLongTimeAM');
MyWorksheet.WriteDateTime(r, 1, now, nfLongTimeAM);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MS');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MS');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, ''mm:ss''');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'mm:ss');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MSZ');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MSZ');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, ''mm:ss.z''');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'mm:ss.z');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, mm:ss.zzz');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, ''mm:ss.zzz''');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'mm:ss.zzz');
// Write formatted numbers
s := '31415.9265359';
@ -301,26 +304,28 @@ begin
MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 3);
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3);
inc(r,2);
MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs');
MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, 'USD');
MyWorksheet.WriteNumber(r, 2, -number, nfCurrency, 0, 'USD');
MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrency, 0, 'USD');
MyWorksheet.WriteCurrency(r, 1, number, nfCurrency, 0, 'USD');
MyWorksheet.WriteCurrency(r, 2, -number, nfCurrency, 0, 'USD');
MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrency, 0, 'USD');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrencyRed, 0 decs');
MyWorksheet.WriteNumber(r, 1, number, nfCurrencyRed, 0, 'USD');
MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyRed, 0, 'USD');
MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 1, number, nfCurrencyRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 2, -number, nfCurrencyRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrencyRed, 0, 'USD');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfAccounting, 0 decs');
MyWorksheet.WriteNumber(r, 1, number, nfAccounting, 0, 'USD');
MyWorksheet.WriteNumber(r, 2, -number, nfAccounting, 0, 'USD');
MyWorksheet.WriteNumber(r, 3, 0.0, nfAccounting, 0, 'USD');
MyWorksheet.WriteCurrency(r, 1, number, nfAccounting, 0, 'USD');
MyWorksheet.WriteCurrency(r, 2, -number, nfAccounting, 0, 'USD');
MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccounting, 0, 'USD');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfAccountingRed, 0 decs');
MyWorksheet.WriteNumber(r, 1, -number, nfAccountingRed, 0, 'USD');
MyWorksheet.WriteNumber(r, 2, number, nfAccountingRed, 0, 'USD');
MyWorksheet.WriteNumber(r, 3, 0.0, nfAccountingRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 1, -number, nfAccountingRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 2, number, nfAccountingRed, 0, 'USD');
MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccountingRed, 0, 'USD');
{
inc(r,2);
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "EUR "#,##0_);("EUR "#,##0)');
MyWorksheet.WriteNumber(r, 1, number);
@ -333,7 +338,7 @@ begin
MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)');
MyWorksheet.WriteNumber(r, 2, -number);
MyWorksheet.WriteNumberFormat(r, 2, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)');
}
inc(r, 2);
number := 1.333333333;
MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 0 decs');
@ -348,29 +353,44 @@ begin
MyWorksheet.WriteUTF8Text(r, 0, 'nfPercentage, 3 decs');
MyWorksheet.WriteNumber(r, 1, number, nfPercentage, 3);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm:ss');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval Default=[h]:mm:ss');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m:s');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:n:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:n:s');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [h]:m:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[h]:m:s');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:mm');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [h]:n:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[h]:n:s');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:nn');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:nn');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [hh]:mm');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[hh]:mm');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [hh]:nn');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[hh]:nn');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:n');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:n');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval [h]:m');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[h]:m');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h');
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [h]:n');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[h]:n');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [h]');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[h]');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [m]:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[m]:s');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, m:s');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'm:s');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [mm]:ss');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[mm]:ss');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, [ss]');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, '[ss]');
// Set width of columns 0, 1 and 5
MyWorksheet.WriteColWidth(0, 30);
@ -395,7 +415,7 @@ begin
MyWorksheet.WriteUTF8Text(0, 3, Str_Fourth);
MyWorksheet.WriteTextRotation(0, 0, rt90DegreeClockwiseRotation);
MyWorksheet.WriteUsedFormatting(0, 1, [uffBold]);
*)
// Save the spreadsheet to a file
MyWorkbook.WriteToFile(MyDir + 'test.xls', sfExcel8, true);
MyWorkbook.Free;

View File

@ -4,7 +4,7 @@ object Form1: TForm1
Top = 248
Width = 884
Caption = 'fpsGrid'
ClientHeight = 624
ClientHeight = 629
ClientWidth = 884
Menu = MainMenu
OnActivate = FormActivate
@ -14,7 +14,7 @@ object Form1: TForm1
object Panel1: TPanel
Left = 0
Height = 85
Top = 539
Top = 544
Width = 884
Align = alBottom
BevelOuter = bvNone
@ -23,9 +23,9 @@ object Form1: TForm1
TabOrder = 0
object CbShowHeaders: TCheckBox
Left = 8
Height = 24
Height = 19
Top = 8
Width = 116
Width = 93
Caption = 'Show headers'
Checked = True
OnClick = CbShowHeadersClick
@ -34,9 +34,9 @@ object Form1: TForm1
end
object CbShowGridLines: TCheckBox
Left = 8
Height = 24
Height = 19
Top = 32
Width = 125
Width = 100
Caption = 'Show grid lines'
Checked = True
OnClick = CbShowGridLinesClick
@ -45,7 +45,7 @@ object Form1: TForm1
end
object EdFrozenCols: TSpinEdit
Left = 389
Height = 28
Height = 23
Top = 8
Width = 52
OnChange = EdFrozenColsChange
@ -53,7 +53,7 @@ object Form1: TForm1
end
object EdFrozenRows: TSpinEdit
Left = 389
Height = 28
Height = 23
Top = 39
Width = 52
OnChange = EdFrozenRowsChange
@ -61,37 +61,37 @@ object Form1: TForm1
end
object Label1: TLabel
Left = 304
Height = 20
Height = 15
Top = 13
Width = 77
Width = 62
Caption = 'Frozen cols:'
FocusControl = EdFrozenCols
ParentColor = False
end
object Label2: TLabel
Left = 304
Height = 20
Height = 15
Top = 40
Width = 82
Width = 66
Caption = 'Frozen rows:'
FocusControl = EdFrozenRows
ParentColor = False
end
object CbReadFormulas: TCheckBox
Left = 8
Height = 24
Height = 19
Top = 56
Width = 120
Width = 96
Caption = 'Read formulas'
OnChange = CbReadFormulasChange
TabOrder = 4
end
object CbHeaderStyle: TComboBox
Left = 152
Height = 28
Height = 23
Top = 8
Width = 116
ItemHeight = 20
ItemHeight = 15
ItemIndex = 2
Items.Strings = (
'Lazarus'
@ -106,7 +106,7 @@ object Form1: TForm1
end
object PageControl1: TPageControl
Left = 0
Height = 460
Height = 465
Top = 79
Width = 884
ActivePage = TabSheet1
@ -116,11 +116,11 @@ object Form1: TForm1
OnChange = PageControl1Change
object TabSheet1: TTabSheet
Caption = 'Sheet1'
ClientHeight = 427
ClientHeight = 437
ClientWidth = 876
object WorksheetGrid: TsWorksheetGrid
Left = 0
Height = 427
Height = 437
Top = 0
Width = 876
FrozenCols = 0
@ -136,7 +136,7 @@ object Form1: TForm1
TitleStyle = tsNative
OnSelection = WorksheetGridSelection
ColWidths = (
56
42
64
64
64
@ -244,19 +244,19 @@ object Form1: TForm1
end
object FontComboBox: TComboBox
Left = 52
Height = 28
Height = 23
Top = 2
Width = 127
ItemHeight = 20
ItemHeight = 15
OnSelect = FontComboBoxSelect
TabOrder = 0
end
object FontSizeComboBox: TComboBox
Left = 179
Height = 28
Height = 23
Top = 2
Width = 48
ItemHeight = 20
ItemHeight = 15
Items.Strings = (
'8'
'9'
@ -2455,70 +2455,70 @@ object Form1: TForm1
OnExecute = AcNumFormatExecute
end
object AcNFFmtDateTimeDM: TAction
Tag = 1111
Tag = 1181
Category = 'Format'
AutoCheck = True
Caption = 'Day + month'
OnExecute = AcNumFormatExecute
end
object AcNFFmtDateTimeMY: TAction
Tag = 1112
Tag = 1182
Category = 'Format'
AutoCheck = True
Caption = 'Month + year'
OnExecute = AcNumFormatExecute
end
object AcNFLongDate: TAction
Tag = 1130
Tag = 1120
Category = 'Format'
AutoCheck = True
Caption = 'Long date'
OnExecute = AcNumFormatExecute
end
object AcNFShortTime: TAction
Tag = 1140
Tag = 1130
Category = 'Format'
AutoCheck = True
Caption = 'Short time'
OnExecute = AcNumFormatExecute
end
object AcNFLongTime: TAction
Tag = 1150
Tag = 1140
Category = 'Format'
AutoCheck = True
Caption = 'Long time'
OnExecute = AcNumFormatExecute
end
object AcNFShortTimeAM: TAction
Tag = 1160
Tag = 1150
Category = 'Format'
AutoCheck = True
Caption = 'Short time AM/PM'
OnExecute = AcNumFormatExecute
end
object AcNFLongTimeAM: TAction
Tag = 1170
Tag = 1160
Category = 'Format'
AutoCheck = True
Caption = 'Long time AM/PM'
OnExecute = AcNumFormatExecute
end
object AcNFFmtDateTimeMS: TAction
Tag = 1113
Tag = 1183
Category = 'Format'
AutoCheck = True
Caption = 'Minutes + seconds'
OnExecute = AcNumFormatExecute
end
object AcNFFmtDateTimeMSZ: TAction
Tag = 1114
Tag = 1184
Category = 'Format'
AutoCheck = True
Caption = 'Minutes + seconds + milliseconds'
OnExecute = AcNumFormatExecute
end
object AcNFTimeInterval: TAction
Tag = 1180
Tag = 1170
Category = 'Format'
AutoCheck = True
Caption = 'Time interval'

View File

@ -271,7 +271,7 @@ var
implementation
uses
fpcanvas, fpsutils;
fpcanvas, fpsutils, fpsnumformatparser;
const
HORALIGN_TAG = 100;
@ -455,17 +455,25 @@ procedure TForm1.AcIncDecDecimalsExecute(Sender: TObject);
var
cell: PCell;
decs: Byte;
parser: TsNumFormatParser;
begin
with WorksheetGrid do begin
if Workbook = nil then
exit;
cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
if (cell <> nil) then begin
decs := cell^.Decimals;
if (Sender = AcIncDecimals) then
Worksheet.WriteDecimals(cell, decs+1);
if (Sender = AcDecDecimals) and (decs > 0) then
Worksheet.WriteDecimals(cell, decs-1);
parser := TsNumFormatParser.Create(Workbook, cell^.NumberFormatStr);
try
decs := parser.Decimals;
if (Sender = AcIncDecimals) then
Parser.Decimals := decs+1;
if (Sender = AcDecDecimals) and (decs > 0) then
Parser.Decimals := decs-1;
cell^.NumberFormatStr := parser.FormatString[nfdDefault];
finally
parser.Free;
end;
Invalidate;
end;
end;
end;
@ -477,23 +485,20 @@ end;
procedure TForm1.AcNumFormatExecute(Sender: TObject);
const
DATETIME_FMT: array[0..4] of string = ('', 'dm', 'my', 'ms', 'msz');
DATETIME_CUSTOM: array[0..4] of string = ('', 'dm', 'my', 'ms', 'msz');
var
nf: TsNumberFormat;
c, r: Cardinal;
cell: PCell;
fmt: String;
begin
if WorksheetGrid.Worksheet = nil then
exit;
if TAction(Sender).Checked then
nf := TsNumberFormat((TAction(Sender).Tag - NUMFMT_TAG) div 10)
else
nf := nfGeneral;
if nf = nfFmtDateTime then
fmt := DATETIME_FMT[TAction(Sender).Tag mod 10]
if nf = nfCustom then
fmt := DATETIME_CUSTOM[TAction(Sender).Tag mod 10]
else
fmt := '';
@ -508,17 +513,22 @@ begin
Worksheet.WriteDateTime(cell, cell^.DateTimeValue, nf, fmt)
else
Worksheet.WriteDateTime(cell, cell^.NumberValue, nf, fmt);
end else
if IsCurrencyFormat(nf) then begin
if IsDateTimeFormat(cell^.NumberFormat) then
Worksheet.WriteCurrency(cell, cell^.DateTimeValue, nf, fmt)
else
Worksheet.WriteCurrency(cell, cell^.Numbervalue, nf, fmt);
end else begin
if IsDateTimeFormat(cell^.NumberFormat) then
Worksheet.WriteNumber(cell, cell^.DateTimeValue, nf, cell^.Decimals, cell^.CurrencySymbol)
Worksheet.WriteNumber(cell, cell^.DateTimeValue, nf, fmt)
else
Worksheet.WriteNumber(cell, cell^.NumberValue, nf, cell^.Decimals, cell^.CurrencySymbol);
Worksheet.WriteNumber(cell, cell^.NumberValue, nf, fmt)
end;
else
Worksheet.WriteNumberformat(cell, nf, fmt);
end;
end;
UpdateNumFormatActions;
end;
@ -872,14 +882,12 @@ begin
t := ac.Tag;
if (ac.Tag >= NUMFMT_TAG) and (ac.Tag < NUMFMT_TAG + 200) then begin
found := ((ac.Tag - NUMFMT_TAG) div 10 = ord(nf));
if (nf = nfFmtDateTime) then
if nf = nfCustom then
case (ac.Tag - NUMFMT_TAG) mod 10 of
1: found := pos('d/m', cell^.NumberFormatStr) > 0;
2: found := pos('m/y', cell^.NumberFormatStr) > 0;
3: found := (pos('n:s', cell^.NumberFormatStr) > 0)
and (pos ('.z', cell^.NumberFormatStr) = 0);
4: found := (pos('n:s', cell^.NumberFormatStr) > 0)
and (pos ('.z', cell^.NumberFormatStr) > 0);
1: found := cell^.NumberFormatStr = 'dd/mmm';
2: found := cell^.NumberFormatStr = 'mmm/yy';
3: found := cell^.NumberFormatStr = 'nn:ss';
4: found := cell^.NumberFormatStr = 'nn:ss.z';
end;
ac.Checked := found;
end;

File diff suppressed because it is too large Load Diff

View File

@ -452,8 +452,6 @@ begin
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := numFmtData.NumFormat;
ACell^.NumberFormatStr := numFmtData.FormatString;
ACell^.Decimals := numFmtData.Decimals;
ACell^.CurrencySymbol := numFmtData.CurrencySymbol;
end;
end;
end;
@ -1074,7 +1072,7 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
if negfmt <> '' then AFormatStr := AFormatStr + ';' + negfmt;
if zerofmt <> '' then AFormatStr := AFormatStr + ';' + zerofmt;
if ANumFormat <> nfFmtDateTime then
// if ANumFormat <> nfFmtDateTime then
ANumFormat := nfCustom;
end;
@ -1141,7 +1139,7 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
else if ANumFormatNode.NodeName = 'number:currency-style' then
nf := nfCurrency;
NumFormatList.AddFormat(ANumFormatName, fmt, nf, decs, cs);
NumFormatList.AddFormat(ANumFormatName, fmt, nf);
end;
procedure ReadDateTimeStyle(ANumFormatNode: TDOMNode; ANumFormatName: String);
@ -1233,7 +1231,8 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
node := node.NextSibling;
end;
nf := IfThen(isInterval, nfTimeInterval, nfFmtDateTime);
// nf := IfThen(isInterval, nfTimeInterval, nfFmtDateTime);
nf := IfThen(isInterval, nfTimeInterval, nfCustom);
node := ANumFormatNode.FindNode('style:map');
if node <> nil then
ReadStyleMap(node, nf, fmt);
@ -1271,9 +1270,11 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
node := ANumFormatNode.FindNode('style:map');
if node <> nil then
ReadStyleMap(node, nf, fmt);
{
if IsDateTimeFormat(fmt) then
nf := nfFmtDateTime
else
}
nf := nfCustom;
NumFormatList.AddFormat(ANumFormatName, fmt, nf);

View File

@ -174,11 +174,46 @@ type
// currency
nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed,
// dates and times
nfShortDateTime, nfFmtDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime,
nfShortDateTime, {nfFmtDateTime, }nfShortDate, nfLongDate, nfShortTime, nfLongTime,
nfShortTimeAM, nfLongTimeAM, nfTimeInterval,
// other (format string goes directly into the file)
nfCustom);
const
{ @@ Codes for curreny format according to FormatSettings.CurrencyFormat:
"C" = currency symbol, "V" = currency value, "S" = space character
For the negative value formats, we use also:
"B" = bracket, "M" = Minus
The order of these characters represents the order of these items.
Example: 1000 dollars --> "$1000" for pCV, or "1000 $" for pVsC
-1000 dollars --> "($1000)" for nbCVb, or "-$ 1000" for nMCSV
Assignment taken from "sysstr.inc" }
pcfDefault = -1; // use value from Worksheet.FormatSettings.CurrencyFormat
pcfCV = 0; // $1000
pcfVC = 1; // 1000$
pcfCSV = 2; // $ 1000
pcfVSC = 3; // 1000 $
ncfDefault = -1; // use value from Worksheet.FormatSettings.NegCurrFormat
ncfBCVB = 0; // ($1000)
ncfMCV = 1; // -$1000
ncfCMV = 2; // $-1000
ncfCVM = 3; // $1000-
ncfBVCB = 4; // (1000$)
ccfMVC = 5; // -1000$
ncfVMC = 6; // 1000-$
ncfVCM = 7; // 1000$-
ncfMVSC = 8; // -1000 $
ncfMCSV = 9; // -$ 1000
ncfVSCM = 10; // 1000 $-
ncfCSVM = 11; // $ 1000-
ncfCSMV = 12; // $ -1000
ncfVMSC = 13; // 1000- $
ncfBCSVB = 14; // ($ 1000)
ncfBVSCB = 15; // (1000 $)
type
{@@ Text rotation formatting. The text is rotated relative to the standard
orientation, which is from left to right horizontal:
--->
@ -328,8 +363,6 @@ type
BackgroundColor: TsColor;
NumberFormat: TsNumberFormat;
NumberFormatStr: String;
Decimals: Byte;
CurrencySymbol: String;
RGBBackgroundColor: TFPColor; // only valid if BackgroundColor=scRGBCOLOR
end;
@ -396,22 +429,9 @@ type
{ Utils }
class function CellPosToText(ARow, ACol: Cardinal): string;
procedure RemoveAllCells;
{ Data manipulation methods - For Cells }
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload;
procedure CopyFormat(AFromCell, AToCell: PCell); overload;
function FindCell(ARow, ACol: Cardinal): PCell;
function GetCell(ARow, ACol: Cardinal): PCell;
function GetCellCount: Cardinal;
function GetFirstCell(): PCell;
function GetNextCell(): PCell;
function GetFirstCellOfRow(ARow: Cardinal): PCell;
function GetLastCellOfRow(ARow: Cardinal): PCell;
function GetLastColIndex: Cardinal;
function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex';
function GetLastRowIndex: Cardinal;
function GetLastRowNumber: Cardinal; deprecated 'Use GetLastRowIndex';
{ Reading of values }
function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload;
function ReadAsUTF8Text(ACell: PCell): ansistring; overload;
function ReadAsNumber(ARow, ACol: Cardinal): Double;
@ -421,25 +441,46 @@ type
function ReadUsedFormatting(ARow, ACol: Cardinal): TsUsedFormattingFields;
function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor;
procedure RemoveAllCells;
{ Writing of values }
procedure WriteBlank(ARow, ACol: Cardinal);
procedure WriteBoolValue(ARow, ACol: Cardinal; AValue: Boolean);
procedure WriteCurrency(ARow, ACol: Cardinal; AValue: Double;
AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = 2;
ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1;
ANegCurrFormat: Integer = -1); overload;
procedure WriteCurrency(ACell: PCell; AValue: Double;
AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1;
ANegCurrFormat: Integer = -1); overload;
procedure WriteCurrency(ARow, ACol: Cardinal; AValue: Double;
AFormat: TsNumberFormat; AFormatString: String); overload;
procedure WriteCurrency(ACell: PCell; AValue: Double;
AFormat: TsNumberFormat; AFormatString: String); overload;
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfGeneral; AFormatStr: String = ''); overload;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); overload;
procedure WriteDateTime(ACell: PCell; AValue: TDateTime;
AFormat: TsNumberFormat = nfGeneral; AFormatStr: String = ''); overload;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); overload;
procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormatStr: String); overload;
procedure WriteDateTime(ACell: PCell; AValue: TDateTime;
AFormatStr: String); overload;
procedure WriteErrorValue(ARow, ACol: Cardinal; AValue: TsErrorValue); overload;
procedure WriteErrorValue(ACell: PCell; AValue: TsErrorValue); overload;
procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula);
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2;
ACurrencySymbol: String = ''); overload;
procedure WriteNumber(ACell: PCell; ANumber: Double; AFormat: TsNumberFormat = nfGeneral;
ADecimals: Byte = 2; ACurrencySymbol: String = ''); overload;
procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double;
AFormatString: String); overload;
AFormat: TsNumberFormat; AFormatString: String); overload;
procedure WriteNumber(ACell: PCell; ANumber: Double;
AFormat: TsNumberFormat; AFormatString: String); overload;
procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula);
procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); overload;
procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload;
@ -483,6 +524,22 @@ type
procedure WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean);
{ Data manipulation methods - For Cells }
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload;
procedure CopyFormat(AFromCell, AToCell: PCell); overload;
function FindCell(ARow, ACol: Cardinal): PCell;
function GetCell(ARow, ACol: Cardinal): PCell;
function GetCellCount: Cardinal;
function GetFirstCell(): PCell;
function GetNextCell(): PCell;
function GetFirstCellOfRow(ARow: Cardinal): PCell;
function GetLastCellOfRow(ARow: Cardinal): PCell;
function GetLastColIndex: Cardinal;
function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex';
function GetLastRowIndex: Cardinal;
function GetLastRowNumber: Cardinal; deprecated 'Use GetLastRowIndex';
{ Data manipulation methods - For Rows and Cols }
function FindRow(ARow: Cardinal): PRow;
function FindCol(ACol: Cardinal): PCol;
@ -506,7 +563,7 @@ type
property Rows: TIndexedAVLTree read FRows;
property Workbook: TsWorkbook read FWorkbook;
// These are properties to interface to fpspreadsheetgrid.
// These are properties to interface to TsWorksheetGrid
property Options: TsSheetOptions read FOptions write FOptions;
property LeftPaneWidth: Integer read FLeftPaneWidth write FLeftPaneWidth;
property TopPaneHeight: Integer read FTopPaneHeight write FTopPaneHeight;
@ -608,8 +665,6 @@ type
Index: Integer;
Name: String;
NumFormat: TsNumberFormat;
Decimals: Byte;
CurrencySymbol: String;
FormatString: string;
end;
@ -630,27 +685,21 @@ type
destructor Destroy; override;
function AddFormat(AFormatCell: PCell): Integer; overload;
function AddFormat(AFormatIndex: Integer; AFormatName, AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer; overload;
ANumFormat: TsNumberFormat): Integer; overload;
function AddFormat(AFormatIndex: Integer; AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer; overload;
ANumFormat: TsNumberFormat): Integer; overload;
function AddFormat(AFormatName, AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer; overload;
function AddFormat(AFormatString: String; ANumFormat: TsNumberFormat;
ADecimals: Byte = 0; ACurrencySymbol: String = ''): Integer; overload;
ANumFormat: TsNumberFormat): Integer; overload;
function AddFormat(AFormatString: String; ANumFormat: TsNumberFormat): Integer; overload;
procedure AnalyzeAndAdd(AFormatIndex: Integer; AFormatString: String);
procedure Clear;
procedure ConvertAfterReading(AFormatIndex: Integer; var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); virtual;
var ANumFormat: TsNumberFormat); virtual;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); virtual;
procedure Delete(AIndex: Integer);
function Find(ANumFormat: TsNumberFormat; AFormatString: String;
ADecimals: Byte; ACurrencySymbol: String): Integer; overload;
function Find(ANumFormat: TsNumberFormat; AFormatString: String): Integer; overload;
function Find(AFormatString: String): Integer; overload;
function FindByIndex(AFormatIndex: Integer): Integer;
function FindByName(AFormatName: String): Integer;
@ -829,6 +878,7 @@ resourcestring
lpUnknownSpreadsheetFormat = 'unknown format';
lpInvalidFontIndex = 'Invalid font index';
lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
lpInvalidDateTimeFormat = 'Trying to use an incompatible date/time format.';
lpNoValidNumberFormatString = 'No valid number format string.';
lpNoValidDateTimeFormatString = 'No valid date/time format string.';
lpNoValidCellAddress = '"%s" is not a valid cell address.';
@ -1129,8 +1179,6 @@ begin
AToCell^.TextRotation := AFromCell^.TextRotation;
AToCell^.NumberFormat := AFromCell^.NumberFormat;
AToCell^.NumberFormatStr := AFromCell^.NumberFormatStr;
AToCell^.Decimals := AFromCell^.Decimals;
AToCell^.CurrencySymbol := AFromCell^.CurrencySymbol;
end;
@ -1321,7 +1369,6 @@ begin
Result^.Col := ACol;
Result^.ContentType := cctEmpty;
Result^.BorderStyles := DEFAULT_BORDERSTYLES;
Result^.CurrencySymbol := '?';
Cells.Add(Result);
end;
@ -1503,7 +1550,7 @@ end;
function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
function FloatToStrNoNaN(const Value: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: string; ADecimals: byte): ansistring;
ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring;
var
fs: TFormatSettings;
left, right: String;
@ -1513,12 +1560,12 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
if IsNan(Value) then
Result := ''
else
if ANumberFormat = nfSci then
Result := SciFloat(Value, ADecimals)
else
if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then
Result := FloatToStr(Value, fs)
else
if ANumberFormat = nfSci then
Result := SciFloat(Value, CountDecs(ANumberFormatStr, ['0']), fs)
else
if (ANumberFormat = nfPercentage) then
Result := FormatFloat(ANumberFormatStr, Value*100, fs)
else
@ -1533,7 +1580,7 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
end;
function DateTimeToStrNoNaN(const Value: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: String; ADecimals: Word): ansistring;
ANumberFormat: TsNumberFormat; ANumberFormatStr: String): ansistring;
var
fmtp, fmtn, fmt0: String;
begin
@ -1564,11 +1611,11 @@ begin
with ACell^ do
case ContentType of
cctNumber:
Result := FloatToStrNoNaN(NumberValue, NumberFormat, NumberFormatStr, Decimals);
Result := FloatToStrNoNaN(NumberValue, NumberFormat, NumberFormatStr);
cctUTF8String:
Result := UTF8StringValue;
cctDateTime:
Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr, Decimals);
Result := DateTimeToStrNoNaN(DateTimeValue, NumberFormat, NumberFormatStr);
cctBool:
Result := IfThen(BoolValue, lpTRUE, lpFALSE);
cctError:
@ -1845,33 +1892,22 @@ begin
WriteNumber(GetCell(ARow, ACol), ANumber, AFormat, ADecimals, ACurrencySymbol);
end;
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double;
AFormat: TsNumberFormat = nfGeneral; ADecimals: Byte = 2;
ACurrencySymbol: String = '');
var
fs: TFormatSettings;
begin
if IsDateTimeFormat(AFormat) or IsCurrencyFormat(AFormat) then
raise Exception.Create(lpInvalidNumberFormat);
if ACell <> nil then begin
ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber;
ACell^.Decimals := ADecimals;
if IsDateTimeFormat(AFormat) then
raise Exception.Create(lpInvalidNumberFormat);
{
if AFormat = nfCustom then
raise Exception.Create(lpIllegalNumberformat);
}
if AFormat <> nfGeneral then begin
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := AFormat;
ACell^.Decimals := ADecimals;
ACell^.CurrencySymbol := ACurrencySymbol;
ACell^.NumberFormatStr := BuildNumberFormatString(ACell^.NumberFormat,
Workbook.FormatSettings, ADecimals, ACurrencySymbol);
Workbook.FormatSettings, ADecimals);
end else begin
Exclude(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := nfGeneral;
@ -1888,36 +1924,41 @@ end;
NOTE that fpspreadsheet may not be able to detect the formatting when reading
the file. }
procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: Double;
AFormatString: String);
AFormat: TsNumberFormat; AFormatString: String);
var
ACell: PCell;
parser: TsNumFormatParser;
nf: TsNumberFormat;
begin
parser := TsNumFormatParser.Create(Workbook, AFormatString, nfCustom, 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;
WriteNumber(GetCell(ARow, ACol), ANumber, AFormat, AFormatString);
end;
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double;
AFormat: TsNumberFormat; AFormatString: String);
var
parser: TsNumFormatParser;
begin
if ACell <> nil then begin
parser := TsNumFormatParser.Create(Workbook, AFormatString);
try
// Format string ok?
if parser.Status <> psOK then
raise Exception.Create(lpNoValidNumberFormatString);
// Make sure that we do not write a date/time value here
if parser.IsDateTimeFormat
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.
finally
parser.Free;
end;
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber;
ACell^.NumberFormat := AFormat; //nfCustom;
ACell^.NumberFormatStr := AFormatString;
ChangedCell(ACell^.Row, ACell^.Col);
end;
ACell := GetCell(ARow, ACol);
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber;
ACell^.NumberFormat := nf;
ACell^.NumberFormatStr := AFormatString;
ACell^.Decimals := 0;
ACell^.CurrencySymbol := '';
ChangedCell(ARow, ACol);
end;
{@@
@ -1954,6 +1995,72 @@ begin
ChangedCell(ARow, ACol);
end;
{@@
Writes a currency value to a given cell. Its number format can be provided
optionally by specifying these parameters:
- ADecimals: number of decimals
- APosCurrFormat: code specifying the order of value, currency symbol and spaces
(see pcfXXXX constants above)
- ANegCurrFormat: code specifying the order of value, currency symbol, spaces
and how negative values are shown (see ncfXXXX constants above)
- ACurrencySymbol: the string to be shown as currency, such as '$', or 'EUR'
}
procedure TsWorksheet.WriteCurrency(ARow, ACol: Cardinal; AValue: Double;
AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = 2;
ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1;
ANegCurrFormat: Integer = -1);
begin
WriteCurrency(GetCell(ARow, ACol), AValue, AFormat, ADecimals, ACurrencySymbol,
APosCurrFormat, ANegCurrFormat);
end;
procedure TsWorksheet.WriteCurrency(ACell: PCell; AValue: Double;
AFormat: TsNumberFormat = nfCurrency; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'; APosCurrFormat: Integer = -1;
ANegCurrFormat: Integer = -1);
var
fmt: String;
begin
if ADecimals = -1 then
ADecimals := Workbook.FormatSettings.CurrencyDecimals;
if APosCurrFormat = -1 then
APosCurrFormat := Workbook.FormatSettings.CurrencyFormat;
if ANegCurrFormat = -1 then
ANegCurrFormat := Workbook.FormatSettings.NegCurrFormat;
if ACurrencySymbol = '?' then
ACurrencySymbol := Workbook.FormatSettings.CurrencyString;
fmt := BuildCurrencyFormatString(
Workbook.FormatSettings,
ADecimals,
APosCurrFormat, ANegCurrFormat,
AFormat in [nfCurrencyRed, nfAccountingRed],
AFormat in [nfAccounting, nfAccountingRed],
ACurrencySymbol);
WriteCurrency(ACell, AValue, AFormat, fmt);
end;
procedure TsWorksheet.WriteCurrency(ARow, ACol: Cardinal; AValue: Double;
AFormat: TsNumberFormat; AFormatString: String);
begin
WriteCurrency(GetCell(ARow, ACol), AValue, AFormat, AFormatString);
end;
procedure TsWorksheet.WriteCurrency(ACell: PCell; AValue: Double;
AFormat: TsNumberFormat; AFormatString: String);
begin
if (ACell <> nil) and IsCurrencyFormat(AFormat) then begin
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.ContentType := cctNumber;
ACell^.NumberValue := AValue;
ACell^.NumberFormat := AFormat;
ACell^.NumberFormatStr := AFormatString;
ChangedCell(ACell^.Row, ACell^.Col);
end;
end;
{@@
Writes a date/time value to a determined cell
@ -1962,24 +2069,23 @@ end;
@param AValue The date/time/datetime to be written
@param AFormat The format specifier, e.g. nfShortDate (optional)
If not specified format is not changed.
@param AFormatStr Format string, used only for nfFmtDateTime.
Must follow the rules for "FormatDateTime", or use
"dm" as abbreviation for "d/mmm", "my" for "mmm/yy",
"ms" for "nn:ss", "msz" for "nn:ss.z" (optional)
or use any other free format (at your own risk...)
@param AFormatStr Format string, used only for nfCustom or nfTimeInterval.
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
as a date (either built-in or a custom format).
}
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfGeneral; AFormatStr: String = '');
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
begin
WriteDateTime(GetCell(ARow, ACol), AValue, AFormat, AFormatStr);
end;
procedure TsWorksheet.WriteDateTime(ACell: PCell; AValue: TDateTime;
AFormat: TsNumberFormat = nfGeneral; AFormatStr: String = '');
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
var
parser: TsNumFormatParser;
nf: TsNumberFormat;
begin
if ACell <> nil then begin
ACell^.ContentType := cctDateTime;
@ -1988,9 +2094,27 @@ begin
// Date/time is actually a number field in Excel.
// To make sure it gets saved correctly, set a date format (instead of General).
// The user can choose another date format if he wants to
if IsDateTimeFormat(AFormat) then
if (AFormat in [nfFmtDateTime, nfTimeInterval]) then
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
if AFormatStr = '' then
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
// Check whether the formatstring is for date/times.
if AFormatStr <> '' then begin
parser := TsNumFormatParser.Create(Workbook, AFormatStr);
try
// Format string ok?
if parser.Status <> psOK then
raise Exception.Create(lpNoValidNumberFormatString);
// Make sure that we do not use a number format for date/times values.
if not parser.IsDateTimeFormat
then raise Exception.Create(lpInvalidDateTimeFormat);
// Avoid possible duplication of standard formats
if AFormat = nfCustom then
AFormat := parser.NumFormat;
finally
parser.Free;
end;
end;
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := AFormat;
@ -1999,24 +2123,42 @@ begin
end;
end;
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormatStr: String);
begin
WriteDateTime(GetCell(ARow, ACol), AValue, AFormatStr);
end;
procedure TsWorksheet.WriteDateTime(ACell: PCell; AValue: TDateTime;
AFormatStr: String);
begin
WriteDateTime(ACell, AValue, nfCustom, AFormatStr);
end;
procedure TsWorksheet.WriteDecimals(ARow, ACol: Cardinal; ADecimals: Byte);
begin
WriteDecimals(FindCell(ARow, ACol), ADecimals);
end;
procedure TsWorksheet.WriteDecimals(ACell: PCell; ADecimals: Byte);
var
parser: TsNumFormatParser;
begin
if (ACell <> nil) and (ACell^.ContentType = cctNumber) and (ACell^.NumberFormat <> nfCustom)
then begin
ACell^.Decimals := ADecimals;
ACell^.NumberFormatStr := BuildNumberFormatString(ACell^.NumberFormat,
FWorkbook.FormatSettings, ADecimals, ACell^.CurrencySymbol);
parser := TsNumFormatParser.Create(Workbook, ACell^.NumberFormatStr);
try
parser.Decimals := ADecimals;
ACell^.NumberFormatStr := parser.FormatString[nfdDefault];
finally
parser.Free;
end;
ChangedCell(ACell^.Row, ACell^.Col);
end;
end;
{@@
Writes a cell with an error.
Writes a cell with an error value.
@param ARow The row of the cell
@param ACol The column of the cell
@ -2037,7 +2179,7 @@ begin
end;
{@@
Writes a formula to a determined cell
Writes a formula to a given cell
@param ARow The row of the cell
@param ACol The column of the cell
@ -2080,8 +2222,7 @@ begin
Include(ACell^.UsedFormattingFields, uffNumberFormat);
ACell^.NumberFormat := ANumberFormat;
if (AFormatString = '') then
ACell^.NumberFormatStr := BuildNumberFormatString(ANumberFormat,
Workbook.FormatSettings, ACell^.Decimals, ACell^.CurrencySymbol)
ACell^.NumberFormatStr := BuildNumberFormatString(ANumberFormat, Workbook.FormatSettings)
else
ACell^.NumberFormatStr := AFormatString;
ChangedCell(ACell^.Row, ACell^.Col);
@ -2521,6 +2662,8 @@ begin
FDefaultColWidth := 12;
FDefaultRowHeight := 1;
FormatSettings := DefaultFormatSettings;
FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat);
FormatSettings.LongDateFormat := MakeLongDateFormat(FormatSettings.ShortDateFormat);
FFontList := TFPList.Create;
SetDefaultFont('Arial', 10.0);
InitFonts;
@ -3194,8 +3337,7 @@ end;
{ Adds a new number format data to the list and returns the list index of the
new item. }
function TsCustomNumFormatList.AddFormat(AFormatIndex: Integer;
AFormatName, AFormatString: String; ANumFormat: TsNumberFormat;
ADecimals: Byte = 0; ACurrencySymbol: String = ''): Integer;
AFormatName, AFormatString: String; ANumFormat: TsNumberFormat): Integer;
var
item: TsNumFormatData;
begin
@ -3203,45 +3345,31 @@ begin
item.Index := AFormatIndex;
item.Name := AFormatName;
item.NumFormat := ANumFormat;
if AFormatString = '' then begin
if IsDateTimeFormat(ANumFormat) then
AFormatString := BuildDateTimeFormatString(ANumFormat, Workbook.FormatSettings,
AFormatString)
else
AFormatString := BuildNumberFormatString(ANumFormat, Workbook.FormatSettings,
ADecimals, ACurrencySymbol);
end;
item.FormatString := AFormatString;
item.Decimals := ADecimals;
item.CurrencySymbol := ACurrencySymbol;
Result := inherited Add(item);
end;
function TsCustomNumFormatList.AddFormat(AFormatIndex: Integer;
AFormatString: String; ANumFormat: TsNumberFormat; ADecimals: byte = 0;
ACurrencySymbol: String = ''): integer;
AFormatString: String; ANumFormat: TsNumberFormat): integer;
begin
Result := AddFormat(AFormatIndex, '', AFormatString, ANumFormat, ADecimals, ACurrencySymbol);
Result := AddFormat(AFormatIndex, '', AFormatString, ANumFormat);
end;
function TsCustomNumFormatList.AddFormat(AFormatName, AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer;
ANumFormat: TsNumberFormat): Integer;
begin
if (AFormatString = '') and (ANumFormat <> nfGeneral) then begin
Result := 0;
exit;
end;
Result := AddFormat(FNextFormatIndex, AFormatName, AFormatString, ANumFormat,
ADecimals, ACurrencySymbol);
Result := AddFormat(FNextFormatIndex, AFormatName, AFormatString, ANumFormat);
inc(FNextFormatIndex);
end;
function TsCustomNumFormatList.AddFormat(AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer;
ANumFormat: TsNumberFormat): Integer;
begin
Result := AddFormat('', AFormatString, ANumFormat, ADecimals, ACurrencySymbol);
Result := AddFormat('', AFormatString, ANumFormat);
end;
function TsCustomNumFormatList.AddFormat(AFormatCell: PCell): Integer;
@ -3256,9 +3384,7 @@ begin
Result := AddFormat(FNextFormatIndex,
AFormatCell^.NumberFormatStr,
AFormatCell^.NumberFormat,
AFormatCell^.Decimals,
AFormatCell^.CurrencySymbol
AFormatCell^.NumberFormat
);
inc(FNextFormatIndex);
@ -3283,8 +3409,7 @@ end;
overridden method which known more about the details of the spreadsheet file
format. }
procedure TsCustomNumFormatList.ConvertAfterReading(AFormatIndex: Integer;
var AFormatString: String; var ANumFormat: TsNumberFormat;
var ADecimals: Byte; var ACurrencySymbol: String);
var AFormatString: String; var ANumFormat: TsNumberFormat);
var
parser: TsNumFormatParser;
fmt: String;
@ -3302,44 +3427,27 @@ begin
nf := nfGeneral;
// Analyzes the format string and tries to convert it to fpSpreadsheet format.
parser := TsNumFormatParser.Create(Workbook, fmt, nf, cdToFPSpreadsheet);
parser := TsNumFormatParser.Create(Workbook, fmt); //, nf, cdToFPSpreadsheet);
try
if parser.Status = psOK then begin
ANumFormat := parser.Builtin_NumFormat;
AFormatString := parser.FormatString; // This is the converted string.
if ANumFormat <> nfCustom then begin
ADecimals := parser.ParsedSections[0].Decimals;
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
end else begin
ADecimals := 0;
ACurrencySymbol := '';
end;
ANumFormat := parser.NumFormat;
AFormatString := parser.FormatString[nfdDefault];
end else begin
// Show an error here?
end;
finally
parser.Free;
end;
end;
{ Is called before collection all number formats of the spreadsheet and before
{ Is called before collecting all number formats of the spreadsheet and before
writing to file. Its purpose is to convert the format string as used by fpc
to a format compatible with the spreadsheet file format. }
to a format compatible with the spreadsheet file format.
Nothing is changed here. The method needs to be overridden. }
procedure TsCustomNumFormatList.ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String);
var
parser: TsNumFormatParser;
fmt: String;
begin
parser := TsNumFormatParser.Create(Workbook, AFormatString, ANumFormat, cdFromFPSpreadsheet);
try
if parser.Status = psOK then begin
AFormatString := parser.FormatString;
ANumFormat := parser.Builtin_NumFormat;
ADecimals := parser.ParsedSections[0].Decimals;
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
end;
finally
parser.Free;
end;
// nothing to do here. But see, e.g., xlscommon.TsBIFFNumFormatList
end;
{ Called from the reader when a format item has been read from the file.
@ -3357,10 +3465,10 @@ begin
exit;
// Analyze & convert the format string, extract infos for internal formatting
ConvertAfterReading(AFormatIndex, AFormatString, nf, decs, currsym);
ConvertAfterReading(AFormatIndex, AFormatString, nf);
// Add the new item
AddFormat(AFormatIndex, AFormatString, nf, decs, currSym);
AddFormat(AFormatIndex, AFormatString, nf);
end;
{ Clears the list and frees memory occupied by the format items. }
@ -3383,65 +3491,14 @@ end;
{ Seeks a format item with the given properties and returns its list index,
or -1 if not found. }
function TsCustomNumFormatList.Find(ANumFormat: TsNumberFormat;
AFormatString: String; ADecimals: Byte; ACurrencySymbol: String): Integer;
AFormatString: String): Integer;
var
item: TsNumFormatData;
fmt: String;
itemfmt: String;
begin
if (ANumFormat = nfFmtDateTime) then begin
fmt := lowercase(AFormatString);
for Result := Count-1 downto 0 do begin
item := Items[Result];
if (item <> nil) and (item.NumFormat = nfFmtDateTime) then begin
itemfmt := lowercase(item.FormatString);
if ((itemfmt = 'dm') or (itemfmt = 'd-mmm') or (itemfmt = 'd mmm') or (itemfmt = 'd. mmm') or (itemfmt ='d/mmm'))
and ((fmt = 'dm') or (fmt = 'd-mmm') or (fmt = 'd mmm') or (fmt = 'd. mmm') or (fmt = 'd/mmm'))
then
exit;
if ((itemfmt = 'my') or (itemfmt = 'mmm-yy') or (itemfmt = 'mmm yyy') or (itemfmt = 'mmm/yy'))
and ((fmt = 'my') or (fmt = 'mmm-yy') or (fmt = 'mmm yy') or (fmt = 'mmm/yy'))
then
exit;
if ((itemfmt = 'ms') or (itemfmt = 'nn:ss') or (itemfmt = 'mm:ss'))
and ((fmt = 'ms') or (fmt = 'nn:ss') or (fmt = 'mm:ss'))
then
exit;
if ((itemfmt = 'msz') or (itemfmt = 'mm:ss.z') or (itemfmt = 'mm:ss.0'))
and ((fmt = 'msz') or (fmt = 'mm:ss.z') or (fmt = 'mm:ss.0'))
then
exit;
end;
end;
for Result := 0 to Count-1 do begin
item := Items[Result];
if fmt = lowercase(item.FormatString) then
exit;
end;
end;
// Check only the format string for nfCustom.
if (ANumFormat = nfCustom) then
for Result := Count-1 downto 0 do begin
item := Items[Result];
if (item <> nil)
and (item.NumFormat = ANumFormat)
and (item.FormatString = AFormatString)
then
exit;
end;
// The other formats can carry additional information
for Result := Count-1 downto 0 do begin
item := Items[Result];
if (item <> nil)
and (item.NumFormat = ANumFormat)
and (item.FormatString = AFormatString)
and (item.Decimals = ADecimals)
and (not (item.NumFormat in [nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed])
or (item.CurrencySymbol = ACurrencySymbol))
then
exit;
if (item <> nil) and (item.NumFormat = ANumFormat) and (item.FormatString = AFormatString)
then exit;
end;
Result := -1;
end;
@ -3499,19 +3556,25 @@ begin
if AFormatCell = nil then
Result := -1
else
Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr,
AFormatCell^.Decimals, AFormatCell^.CurrencySymbol);
Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr);
end;
{ Determines the format string to be written into the spreadsheet file.
Needs to be overridden if the format strings are different from the fpc
convention. }
{ Determines the format string to be written into the spreadsheet file. Calls
ConvertBeforeWriting in order to convert the fpc format strings to the dialect
used in the file. }
function TsCustomNumFormatList.FormatStringForWriting(AIndex: Integer): String;
var
item: TsNumFormatdata;
nf: TsNumberFormat;
decs: Byte;
cs: String;
begin
item := Items[AIndex];
if item <> nil then Result := item.FormatString else Result := '';
if item <> nil then begin
Result := item.FormatString;
ConvertBeforeWriting(Result, nf, decs, cs);
end else
Result := '';
end;
function TsCustomNumFormatList.GetItem(AIndex: Integer): TsNumFormatData;
@ -3641,14 +3704,14 @@ end;
}
function TsCustomSpreadWriter.FindFormattingInList(AFormat: PCell): Integer;
var
i: Integer;
i, n: Integer;
b: TsCellBorder;
equ: Boolean;
begin
Result := -1;
for i := Length(FFormattingStyles) - 1 downto 0 do
begin
n := Length(FFormattingStyles);
for i := n - 1 downto 0 do begin
if (FFormattingStyles[i].UsedFormattingFields <> AFormat^.UsedFormattingFields) then Continue;
if uffHorAlign in AFormat^.UsedFormattingFields then
@ -3683,18 +3746,7 @@ begin
if uffNumberFormat in AFormat^.UsedFormattingFields then begin
if (FFormattingStyles[i].NumberFormat <> AFormat^.NumberFormat) then Continue;
case AFormat^.NumberFormat of
nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci:
if (FFormattingStyles[i].Decimals <> AFormat^.Decimals) then Continue;
nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed:
begin
if (FFormattingStyles[i].Decimals <> AFormat^.Decimals) then Continue;
if (FFormattingStyles[i].CurrencySymbol <> AFormat^.CurrencySymbol) then Continue;
end;
nfShortDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime,
nfShortTimeAM, nfLongTimeAM, nfFmtDateTime, nfTimeInterval, nfCustom:
if (FFormattingstyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue;
end;
if (FFormattingStyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue;
end;
if uffFont in AFormat^.UsedFormattingFields then
@ -3740,23 +3792,6 @@ begin
SetLength(FFormattingStyles, Len+1);
FFormattingStyles[Len] := ACell^;
// Some built-in number formats do not write the format string to the cell
// But the FormattingStyles need it for comparison later. --> Add the format string.
if IsDateTimeFormat(FFormattingStyles[Len].NumberFormat) then
FFormattingStyles[Len].NumberFormatStr := BuildDateTimeFormatString(
FFormattingStyles[Len].NumberFormat,
Workbook.FormatSettings,
FFormattingStyles[Len].NumberFormatStr
)
else
if FFormattingStyles[Len].NumberFormat <> nfCustom then
FFormattingstyles[Len].NumberFormatStr := BuildNumberFormatString(
FFormattingStyles[Len].NumberFormat,
Workbook.FormatSettings,
FFormattingStyles[Len].Decimals,
FFormattingStyles[Len].CurrencySymbol
);
// We store the index of the XF record that will be assigned to this style in
// the "row" of the style. Will be needed when writing the XF record.
FFormattingStyles[Len].Row := NextXFIndex;
@ -3776,6 +3811,7 @@ begin
for i := 0 to Workbook.GetWorksheetCount - 1 do
IterateThroughCells(nil, Workbook.GetWorksheetByIndex(i).Cells, ListAllFormattingStylesCallback);
(*
// Convert the numberformats of the collected styles to be compatible with the destination file
for i:=0 to High(FFormattingStyles) do
if (FFormattingStyles[i].NumberFormatStr <> '') and
@ -3787,6 +3823,7 @@ begin
FFormattingStyles[i].Decimals,
FFormattingStyles[i].CurrencySymbol
);
*)
end;
{@@
@ -3797,37 +3834,25 @@ procedure TsCustomSpreadWriter.ListAllNumFormatsCallback(ACell: PCell; AStream:
var
fmt: string;
nf: TsNumberFormat;
decs: Byte;
cs: String;
begin
if ACell^.NumberFormat = nfGeneral then
exit;
// The builtin format list is in "file syntax", but the format string of the
// cells are in "fpc syntax". Therefore, before seeking, we have to convert
// the format string of the cell to "file syntax".
// The builtin format list is in fpc dialect.
fmt := ACell^.NumberFormatStr;
nf := ACell^.NumberFormat;
decs := ACell^.Decimals;
cs := ACell^.CurrencySymbol;
if (nf <> nfCustom) then begin
if IsDateTimeFormat(nf) then
fmt := BuildDateTimeFormatString(nf, Workbook.FormatSettings, fmt)
else
fmt := BuildNumberFormatString(nf, Workbook.FormatSettings, decs, cs);
FNumFormatList.ConvertBeforeWriting(fmt, nf, decs, cs);
end;
// Seek the format string in the current number format list.
// If not found add the format to the list.
if FNumFormatList.Find(fmt) = -1 then
FNumFormatList.AddFormat(fmt, nf, decs, cs);
if FNumFormatList.Find(nf, fmt) = -1 then
FNumFormatList.AddFormat(fmt, nf);
end;
{@@
Iterats through all cells and collects the number formats in
Iterates through all cells and collects the number formats in
FNumFormatList (without duplicates).
The index of the list item is needed for the field FormatIndex of the XF record. }
The index of the list item is needed for the field FormatIndex of the XF record.
At the time when the method is called the formats are still in fpc dialect. }
procedure TsCustomSpreadWriter.ListAllNumFormats;
var
i: Integer;

View File

@ -70,22 +70,23 @@ function IfThen(ACondition: Boolean; AValue1,AValue2: TsNumberFormat): TsNumberF
function IsCurrencyFormat(AFormat: TsNumberFormat): Boolean;
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean; overload;
function IsDateTimeFormat(AFormatStr: String): Boolean; overload;
//function IsDateTimeFormat(AFormatStr: String): Boolean; overload;
function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'): String;
function BuildCurrencyFormatString(const AFormatSettings: TFormatSettings;
ADecimals, APosCurrFormat, ANegCurrFormat: Integer;
ANegativeValuesRed, AAccountingStyle: Boolean; ACurrencySymbol: String = '?'): String;
function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; AFormatString: String = ''): String;
function BuildCurrencyFormatString(const AFormatSettings: TFormatSettings;
ADecimals: Integer; ANegativeValuesRed: Boolean; AAccountingStyle: Boolean;
ACurrencySymbol: String = '?'): String;
function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1): String;
function AddAMPM(const ATimeFormatString: String;
const AFormatSettings: TFormatSettings): String;
function StripAMPM(const ATimeFormatString: String): String;
function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte;
function AddIntervalBrackets(AFormatString: String): String;
function MakeLongDateFormat(AShortDateFormat: String): String;
function MakeShortDateFormat(AShortDateFormat: String): String;
function SpecialDateTimeFormat(ACode: String;
const AFormatSettings: TFormatSettings; ForWriting: Boolean): String;
function SplitAccountingFormatString(const AFormatString: String; ASection: ShortInt;
@ -93,7 +94,8 @@ function SplitAccountingFormatString(const AFormatString: String; ASection: Shor
procedure SplitFormatString(const AFormatString: String; out APositivePart,
ANegativePart, AZeroPart: String);
function SciFloat(AValue: Double; ADecimals: Byte): String;
function SciFloat(AValue: Double; ADecimals: Byte): String; overload;
function SciFloat(AValue: Double; ADecimals: Byte; AFormatSettings: TFormatSettings): String; overload;
//function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String;
procedure MakeTimeIntervalMask(Src: String; var Dest: String);
@ -563,53 +565,10 @@ end;
{ Checks whether the given number format code is for date/times. }
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean;
begin
Result := AFormat in [nfFmtDateTime, nfShortDateTime, nfShortDate, nfLongDate,
Result := AFormat in [{nfFmtDateTime, }nfShortDateTime, nfShortDate, nfLongDate,
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval];
end;
function IsDateTimeFormat(AFormatStr: string): Boolean;
var
P, PStart, PEnd: PChar;
token: Char;
begin
if AFormatStr = '' then
Result := false
else begin
PStart := PChar(@AFormatStr[1]);
PEnd := PStart + Length(AFormatStr);
P := PStart;
while P < PEnd do begin
token := P^;
case token of // Skip quoted text
'"': begin
inc(P);
token := P^;
while (P < PEnd) and (token <> '"') do begin
inc(P);
token := P^;
end;
end;
{
'[': begin
inc(P);
token := P^;
while (P < PEnd) and (token <> ']') do begin
inc(P);
token := P^;
end;
end;
}
'y', 'Y', 'm', 'M', 'd', 'D', 'h', 'H', 'n', 'N', 's', 'S', ':':
begin
Result := true;
exit;
end;
end;
inc(P);
end;
end;
end;
{ Builds a date/time format string from the numberformat code. If the format code
is nfFmtDateTime the given AFormatString is used. AFormatString can use the
abbreviations "dm" (for "d/mmm"), "my" (for "mmm/yy"), "ms" (for "mm:ss")
@ -620,8 +579,10 @@ var
fmt: String;
begin
case ANumberFormat of
{
nfFmtDateTime:
Result := SpecialDateTimeFormat(lowercase(AFormatString), AFormatSettings, false);
}
nfShortDateTime:
Result := AFormatSettings.ShortDateFormat + ' ' + AFormatSettings.ShortTimeFormat;
// In the DefaultFormatSettings this is: d/m/y hh:nn
@ -664,41 +625,44 @@ end;
This code has to be removed by StripAccountingSymbols before applying to
FormatFloat. }
function BuildCurrencyFormatString(const AFormatSettings: TFormatSettings;
ADecimals: Integer; ANegativeValuesRed: Boolean; AAccountingStyle: Boolean;
ACurrencySymbol: String = '?'): String;
ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ANegativeValuesRed: Boolean;
AAccountingStyle: Boolean; ACurrencySymbol: String = '?'): String;
const
POS_FMT: array[0..3, boolean] of string = ( //0: value, 1: currency symbol
POS_FMT: array[0..3, boolean] of string = (
// Parameter 0 is "value", parameter 1 is "currency symbol"
// AccountingStyle = false --> 1st column, true --> 2nd column
('"%1:s"%0:s', '"%1:s"* %0:s'), // 0: $1
('%0:s"%1:s"', '%0:s* "%1:s"'), // 1: 1$
('%0:s"%1:s"', '%0:s "%1:s"'), // 1: 1$
('"%1:s" %0:s', '"%1:s"* %0:s'), // 2: $ 1
('%0:s "%1:s"', '%0:s* "%1:s"') // 3: 1 $
('%0:s "%1:s"', '%0:s "%1:s"') // 3: 1 $
);
NEG_FMT: array[0..15, boolean] of string = (
('("%1:s"%0:s)', '"%1:s"* (%0:s)'), // 0: ($1)
('-"%1:s"%0:s', '"%1:s"* -%0:s'), // 1: -$1
('-"%1:s"%0:s', '-* "%1:s" %0:s'), // 1: -$1
('"%1:s"-%0:s', '"%1:s"* -%0:s'), // 2: $-1
('"%1:s"%0:s-', '"%1:s"* %0:s-'), // 3: $1-
('(%0:s"%1:s")', '(%0:s)"%1:s"'), // 4: (1$)
('-%0:s"%1:s"', '-%0:s"%1:s"'), // 5: -1$
('"%1:s"%0:s-', '"%1:s"%0:s-'), // 3: $1-
('(%0:s"%1:s")', '(%0:s)%1:s"'), // 4: (1$)
('-%0:s"%1:s"', '-* %0:s"%1:s"'), // 5: -1$
('%0:s-"%1:s"', '%0:s-"%1:s"'), // 6: 1-$
('%0:s"%1:s"-', '%0:s-"%1:s"'), // 7: 1$-
('-%0:s "%1:s"', '-%0:s"%1:s"'), // 8: -1 $
('-"%1:s" %0:s', '"%1:s"* -%0:s'), // 9: -$ 1
('-%0:s "%1:s"', '-* %0:s"%1:s"'), // 8: -1 $
('-"%1:s" %0:s', '-* "%1:s" -%0:s'), // 9: -$ 1
('%0:s "%1:s"-', '%0:s- "%1:s"'), // 10: 1 $-
('"%1:s" %0:s-', '"%1:s"* %0:s-'), // 11: $ 1-
('"%1:s" -%0:s', '"%1:s"* -%0:s'), // 12: $ -1
('%0:s- "%1:s"', '%0:s- "%1:s"'), // 13: 1- $
('("%1:s" %0:s)', '"%1:s"* (%0:s)'), // 14: ($ 1)
('(%0:s "%1:s")', '(%0:s) "%1:s"') // 15: (1 $)
('(%0:s "%1:s")', '(%0:s "%1:s")') // 15: (1 $)
);
var
decs: String;
cf, ncf: Byte;
p, n: String;
begin
cf := AFormatSettings.CurrencyFormat;
ncf := AFormatSettings.NegCurrFormat;
if ADecimals < 0 then ADecimals := AFormatSettings.CurrencyDecimals;
cf := IfThen(APosCurrFormat < 0, AFormatSettings.CurrencyFormat, APosCurrFormat);
ncf := IfThen(ANegCurrFormat < 0, AFormatSettings.NegCurrFormat, ANegCurrFormat);
if ADecimals < 0 then
ADecimals := AFormatSettings.CurrencyDecimals;
if ACurrencySymbol = '?' then
ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString);
decs := DupeString('0', ADecimals);
@ -717,7 +681,7 @@ begin
if ACurrencySymbol <> '' then begin
Result := Format(p, ['#,##0' + decs, ACurrencySymbol]) + ';'
+ Format(n, ['#,##0' + decs, ACurrencySymbol]) + ';'
+ IfThen(ANegativeValuesRed, '[red]', '') + Format(n, ['#,##0' + decs, ACurrencySymbol]) + ';'
+ Format(p, [IfThen(AAccountingStyle, '-', '0'+decs), ACurrencySymbol]);
end
else begin
@ -731,21 +695,16 @@ begin
end;
end;
{ Builds a number format string from the numberformat code, the count of
{ Builds a number format string from the number format code, the count of
decimals, and the currencysymbol (if not empty). }
function BuildNumberFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1;
ACurrencySymbol: String = '?'): String;
const AFormatSettings: TFormatSettings; ADecimals: Integer = -1): String;
var
decs: String;
cf, ncf: Byte;
begin
Result := '';
cf := AFormatSettings.CurrencyFormat;
ncf := AFormatSettings.NegCurrFormat;
if ADecimals = -1 then ADecimals := AFormatSettings.CurrencyDecimals;
if ACurrencySymbol = '?' then
ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString);
if ADecimals = -1 then
ADecimals := AFormatSettings.CurrencyDecimals;
decs := DupeString('0', ADecimals);
if ADecimals > 0 then decs := '.' + decs;
case ANumberFormat of
@ -760,13 +719,12 @@ begin
nfPercentage:
Result := '0' + decs + '%';
nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed:
Result := BuildCurrencyFormatString(
AFormatSettings,
ADecimals,
ANumberFormat in [nfCurrencyRed, nfAccountingRed],
ANumberFormat in [nfAccounting, nfAccountingRed],
ACurrencySymbol
);
raise Exception.Create('BuildNumberFormatString: Use BuildCurrencyFormatString '+
'to create a format string for currency values.');
nfShortDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime,
nfShortTimeAM, nfLongTimeAM, nfTimeInterval:
raise Exception.Create('BuildNumberFormatString: Use BuildDateTimeFormatSstring '+
'to create a format string for date/time values.');
end;
end;
@ -802,12 +760,18 @@ 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;
i := 1;
while (i <= Length(AFormatString)) do begin
if AFormatString[i] = '.' then begin
inc(i);
while (i <= Length(AFormatString)) and (AFormatString[i] in ADecChars) do begin
inc(i);
inc(Result);
end;
exit;
end else
inc(i);
end;
// Comes to this point when there is no decimal separtor.
Result := 0;
end;
{ The given format string is assumed to be for time intervals, i.e. its first
@ -831,6 +795,64 @@ begin
end;
end;
{ Creates a long date format string out of a short one. Retains the order of
year-month-day and the separators, but uses 4 digits for year and 3 digits of m }
function MakeLongDateFormat(AShortDateFormat: String): String;
var
i: Integer;
begin
Result := '';
i := 1;
while i < Length(AShortDateFormat) do begin
case AShortDateFormat[i] of
'y', 'Y':
begin
Result := Result + DupeString(AShortDateFormat[i], 4);
while (i < Length(AShortDateFormat)) and (AShortDateFormat[i] in ['y','Y']) do
inc(i);
end;
'm', 'M':
begin
result := Result + DupeString(AShortDateFormat[i], 3);
while (i < Length(AShortDateFormat)) and (AShortDateFormat[i] in ['m','M']) do
inc(i);
end;
else
Result := Result + AShortDateFormat[i];
inc(i);
end;
end;
end;
{ Modifies the short date format such that it has a two-digit year and a two-digit
month. Retains the order of year-month-day and the separators. }
function MakeShortDateFormat(AShortDateFormat: String): String;
var
i: Integer;
begin
Result := '';
i := 1;
while i < Length(AShortDateFormat) do begin
case AShortDateFormat[i] of
'y', 'Y':
begin
Result := Result + DupeString(AShortDateFormat[i], 2);
while (i < Length(AShortDateFormat)) and (AShortDateFormat[i] in ['y','Y']) do
inc(i);
end;
'm', 'M':
begin
result := Result + DupeString(AShortDateFormat[i], 2);
while (i < Length(AShortDateFormat)) and (AShortDateFormat[i] in ['m','M']) do
inc(i);
end;
else
Result := Result + AShortDateFormat[i];
inc(i);
end;
end;
end;
{ Creates the formatstrings for the date/time codes "dm", "my", "ms" and "msz"
out of the formatsettings. }
function SpecialDateTimeFormat(ACode: String;
@ -862,7 +884,7 @@ begin
Result := DupeString(MinuteChar, 2) + ':ss'; // mm:ss
end
else if ACode = 'msz' then
Result := DupeString(MinuteChar, 2) + ':ss.' + MillisecChar // mm:ss.z
Result := DupeString(MinuteChar, 2) + ':ss.' + MillisecChar // mm:ss.z
else
Result := ACode;
end;
@ -991,23 +1013,30 @@ end;
{ Formats the number AValue in "scientific" format with the given number of
decimals. "Scientific" is the same as "exponential", but with exponents rounded
to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). }
function SciFloat(AValue: Double; ADecimals: Byte): String;
function SciFloat(AValue: Double; ADecimals: Byte;
AFormatSettings: TFormatSettings): String;
var
m: Double;
ex: Integer;
begin
if AValue = 0 then
Result := '0.0'
Result := Format('%0.*fE+0', [ADecimals, 0.0], AFormatSettings)
// Excel shows "000.0E+0", but I think the "0.0E+0" shown here is better.
else begin
ex := floor(log10(abs(AValue))); // exponent
// round exponent to multiples of 3
ex := (ex div 3) * 3;
if ex < 0 then dec(ex, 3);
m := AValue * Power(10, -ex); // mantisse
Result := Format('%.*fE%d', [ADecimals, m, ex]);
Result := Format('%.*fE+%d', [ADecimals, m, ex], AFormatSettings);
end;
end;
function SciFloat(AValue: Double; ADecimals: Byte): String;
begin
Result := SciFloat(AValue, ADecimals, DefaultFormatSettings);
end;
{ Creates a "time interval" format string having the first code identifier
in square brackets. }
procedure MakeTimeIntervalMask(Src: String; var Dest: String);

View File

@ -250,27 +250,27 @@ begin
SollDates[14]:=SollDates[1]; // #1 formatted as nfLongTime
SollDates[15]:=SollDates[1]; // #1 formatted as nfShortTimeAM
SollDates[16]:=SollDates[1]; // #1 formatted as nfLongTimeAM
SollDates[17]:=SollDates[1]; // #1 formatted as nfFmtDateTime dm
SollDates[18]:=SollDates[1]; // #1 formatted as nfFmtDateTime my
SollDates[19]:=SollDates[1]; // #1 formatted as nfFmtDateTime ms
SollDates[17]:=SollDates[1]; // #1 formatted as nfCustom dd/mmm
SollDates[18]:=SollDates[1]; // #1 formatted as nfCustom mmm/yy
SollDates[19]:=SollDates[1]; // #1 formatted as nfCustom mm:ss
SollDates[20]:=SollDates[5]; // #5 formatted as nfShortDateTime
SollDates[21]:=SollDates[5]; // #5 formatted as nfShortTime
SollDates[22]:=SollDates[5]; // #5 formatted as nfLongTime
SollDates[23]:=SollDates[5]; // #5 formatted as nfShortTimeAM
SollDates[24]:=SollDates[5]; // #5 formatted as nfLongTimeAM
SollDates[25]:=SollDates[5]; // #5 formatted as nfFmtDateTime dm
SollDates[26]:=SollDates[5]; // #5 formatted as nfFmtDateTime my
SollDates[27]:=SollDates[5]; // #5 formatted as nfFmtDateTime ms
SollDates[25]:=SollDates[5]; // #5 formatted as nfCustom dd:mmm
SollDates[26]:=SollDates[5]; // #5 formatted as nfCustom mmm:yy
SollDates[27]:=SollDates[5]; // #5 formatted as nfCustom mm:ss
SollDates[28]:=SollDates[11]; // #11 formatted as nfShortDateTime
SollDates[29]:=SollDates[11]; // #11 formatted as nfShortTime
SollDates[30]:=SollDates[11]; // #11 formatted as nfLongTime
SollDates[31]:=SollDates[11]; // #11 formatted as nfShortTimeAM
SollDates[32]:=SollDates[11]; // #11 formatted as nfLongTimeAM
SollDates[33]:=SollDates[11]; // #11 formatted as nfFmtDateTime dm
SollDates[34]:=SollDates[11]; // #11 formatted as nfFmtDateTime my
SollDates[35]:=SollDates[11]; // #11 formatted as nfFmtDateTime ms
SollDates[33]:=SollDates[11]; // #11 formatted as nfCustom dd/mmm
SollDates[34]:=SollDates[11]; // #11 formatted as nfCustom mmm/yy
SollDates[35]:=SollDates[11]; // #11 formatted as nfCustom mmm:ss
SollDates[36]:=EncodeTime(3,45,12,0); // formatted as nfTimeDuration
SollDates[37]:=EncodeTime(3,45,12,0) + 1 // formatted as nfTimeDuration

View File

@ -137,10 +137,15 @@ procedure InitSollFmtData;
var
i: Integer;
fs: TFormatSettings;
myworkbook: TsWorkbook;
begin
// Set up norm - MUST match spreadsheet cells exactly
fs := DefaultFormatSettings;
// The workbook uses a slightly modified copy of the DefaultFormatSettings
// We create a copy here in order to better define the predicted strings.
myWorkbook := TsWorkbook.Create;
fs := MyWorkbook.FormatSettings;
myWorkbook.Free;
// Numbers
SollNumbers[0] := 0.0;
@ -162,15 +167,15 @@ begin
SollNumberFormats[8] := nfSci; SollNumberDecimals[8] := 1;
for i:=Low(SollNumbers) to High(SollNumbers) do begin
SollNumberStrings[i, 0] := FloatToStr(SollNumbers[i]);
SollNumberStrings[i, 1] := FormatFloat('0', SollNumbers[i]);
SollNumberStrings[i, 2] := FormatFloat('0.00', SollNumbers[i]);
SollNumberStrings[i, 3] := FormatFloat('#,##0', SollNumbers[i]);
SollNumberStrings[i, 4] := FormatFloat('#,##0.00', SollNumbers[i]);
SollNumberStrings[i, 5] := FormatFloat('0.00E+00', SollNumbers[i]);
SollNumberStrings[i, 6] := FormatFloat('0', SollNumbers[i]*100) + '%';
SollNumberStrings[i, 7] := FormatFloat('0.00', SollNumbers[i]*100) + '%';
SollNumberStrings[i, 8] := SciFloat(SollNumbers[i], 1);
SollNumberStrings[i, 0] := FloatToStr(SollNumbers[i], fs);
SollNumberStrings[i, 1] := FormatFloat('0', SollNumbers[i], fs);
SollNumberStrings[i, 2] := FormatFloat('0.00', SollNumbers[i], fs);
SollNumberStrings[i, 3] := FormatFloat('#,##0', SollNumbers[i], fs);
SollNumberStrings[i, 4] := FormatFloat('#,##0.00', SollNumbers[i], fs);
SollNumberStrings[i, 5] := FormatFloat('0.00E+00', SollNumbers[i], fs);
SollNumberStrings[i, 6] := FormatFloat('0', SollNumbers[i]*100, fs) + '%';
SollNumberStrings[i, 7] := FormatFloat('0.00', SollNumbers[i]*100, fs) + '%';
SollNumberStrings[i, 8] := SciFloat(SollNumbers[i], 1, fs);
end;
// Date/time values
@ -186,22 +191,22 @@ begin
SollDateTimeFormats[3] := nfLongTime; SollDateTimeFormatStrings[3] := '';
SollDateTimeFormats[4] := nfShortTimeAM; SollDateTimeFormatStrings[4] := '';
SollDateTimeFormats[5] := nfLongTimeAM; SollDateTimeFormatStrings[5] := '';
SollDateTimeFormats[6] := nfFmtDateTime; SollDateTimeFormatStrings[6] := 'dm';
SolLDateTimeFormats[7] := nfFmtDateTime; SollDateTimeFormatStrings[7] := 'my';
SollDateTimeFormats[8] := nfFmtDateTime; SollDateTimeFormatStrings[8] := 'ms';
SollDateTimeFormats[6] := nfCustom; SollDateTimeFormatStrings[6] := 'dd/mmm';
SolLDateTimeFormats[7] := nfCustom; SollDateTimeFormatStrings[7] := 'mmm/yy';
SollDateTimeFormats[8] := nfCustom; SollDateTimeFormatStrings[8] := 'nn:ss';
SollDateTimeFormats[9] := nfTimeInterval; SollDateTimeFormatStrings[9] := '';
for i:=Low(SollDateTimes) to High(SollDateTimes) do begin
SollDateTimeStrings[i, 0] := DateToStr(SollDateTimes[i]) + ' ' + FormatDateTime('t', SollDateTimes[i]);
SollDateTimeStrings[i, 1] := DateToStr(SollDateTimes[i]);
SollDateTimeStrings[i, 2] := FormatDateTime(fs.ShortTimeFormat, SollDateTimes[i]);
SolLDateTimeStrings[i, 3] := FormatDateTime(fs.LongTimeFormat, SollDateTimes[i]);
SollDateTimeStrings[i, 4] := FormatDateTime(fs.ShortTimeFormat + ' am/pm', SollDateTimes[i]); // dont't use "t" - it does the hours wrong
SollDateTimeStrings[i, 5] := FormatDateTime(fs.LongTimeFormat + ' am/pm', SollDateTimes[i]);
SollDateTimeStrings[i, 6] := FormatDateTime(SpecialDateTimeFormat('dm', fs, false), SollDateTimes[i]);
SollDateTimeStrings[i, 7] := FormatDateTime(SpecialDateTimeFormat('my', fs, false), SollDateTimes[i]);
SollDateTimeStrings[i, 8] := FormatDateTime(SpecialDateTimeFormat('ms', fs, false), SollDateTimes[i]);
SollDateTimeStrings[i, 9] := FormatDateTime('[h]:mm:ss', SollDateTimes[i], [fdoInterval]);
SollDateTimeStrings[i, 0] := DateToStr(SollDateTimes[i], fs) + ' ' + FormatDateTime('t', SollDateTimes[i], fs);
SollDateTimeStrings[i, 1] := DateToStr(SollDateTimes[i], fs);
SollDateTimeStrings[i, 2] := FormatDateTime(fs.ShortTimeFormat, SollDateTimes[i], fs);
SolLDateTimeStrings[i, 3] := FormatDateTime(fs.LongTimeFormat, SollDateTimes[i], fs);
SollDateTimeStrings[i, 4] := FormatDateTime(fs.ShortTimeFormat + ' am/pm', SollDateTimes[i], fs); // dont't use "t" - it does the hours wrong
SollDateTimeStrings[i, 5] := FormatDateTime(fs.LongTimeFormat + ' am/pm', SollDateTimes[i], fs);
SollDateTimeStrings[i, 6] := FormatDateTime(SpecialDateTimeFormat('dm', fs, false), SollDateTimes[i], fs);
SollDateTimeStrings[i, 7] := FormatDateTime(SpecialDateTimeFormat('my', fs, false), SollDateTimes[i], fs);
SollDateTimeStrings[i, 8] := FormatDateTime(SpecialDateTimeFormat('ms', fs, false), SollDateTimes[i], fs);
SollDateTimeStrings[i, 9] := FormatDateTime('[h]:mm:ss', SollDateTimes[i], fs, [fdoInterval]);
end;
// Column width
@ -345,7 +350,7 @@ begin
MyWorksheet := MyWorkbook.AddWorksheet(FmtDateTimesSheet);
for Row := Low(SollDateTimes) to High(SollDateTimes) do
for Col := Low(SollDateTimeFormats) to High(SollDateTimeFormats) do begin
if (AFormat = sfExcel2) and (SollDateTimeFormats[Col] in [nfFmtDateTime, nfTimeInterval]) then
if (AFormat = sfExcel2) and (SollDateTimeFormats[Col] in [nfCustom, nfTimeInterval]) then
Continue; // The formats nfFmtDateTime and nfTimeInterval are not supported by BIFF2
MyWorksheet.WriteDateTime(Row, Col, SollDateTimes[Row], SollDateTimeFormats[Col], SollDateTimeFormatStrings[Col]);
ActualString := MyWorksheet.ReadAsUTF8Text(Row, Col);
@ -369,7 +374,7 @@ begin
fail('Error in test code. Failed to get named worksheet');
for Row := Low(SollDateTimes) to High(SollDateTimes) do
for Col := Low(SollDateTimeFormats) to High(SollDateTimeFormats) do begin
if (AFormat = sfExcel2) and (SollDateTimeFormats[Col] in [nfFmtDateTime, nfTimeInterval]) then
if (AFormat = sfExcel2) and (SollDateTimeFormats[Col] in [nfCustom, nfTimeInterval]) then
Continue; // The formats nfFmtDateTime and nfTimeInterval are not supported by BIFF2
ActualString := MyWorksheet.ReadAsUTF8Text(Row,Col);
CheckEquals(

View File

@ -46,6 +46,7 @@ implementation
uses
TypInfo;
{ The test will use Excel strings and convert them to fpc dialect }
procedure InitParserTestData;
begin
// Tests with 1 format section only
@ -146,14 +147,16 @@ var
i: Integer;
parser: TsNumFormatParser;
MyWorkbook: TsWorkbook;
actual: String;
begin
MyWorkbook := TsWorkbook.Create; // needed to provide the FormatSettings for the parser
try
for i:=0 to 5 do begin
parser := TsNumFormatParser.Create(MyWorkbook, ParserTestData[i].FormatString, cdToFPSpreadsheet);
parser := TsNumFormatParser.Create(MyWorkbook, ParserTestData[i].FormatString);
try
CheckEquals(ParserTestData[i].SollFormatString, parser.FormatString,
'Test format string ' + ParserTestData[i].FormatString + ' construction mismatch');
actual := parser.FormatString[nfdDefault];
CheckEquals(ParserTestData[i].SollFormatString, actual,
'Test format string ' + ParserTestData[i].SollFormatString + ' construction mismatch');
CheckEquals(ord(ParserTestData[i].SollNumFormat), ord(parser.ParsedSections[0].NumFormat),
'Test format (' + GetEnumName(TypeInfo(TsNumberFormat), integer(ParserTestData[i].SollNumFormat)) +
') detection mismatch');

View File

@ -48,9 +48,6 @@
</Debugging>
</Linking>
<Other>
<CompilerMessages>
<MsgFileName Value=""/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
@ -173,9 +170,6 @@
</Optimizations>
</CodeGeneration>
<Other>
<CompilerMessages>
<MsgFileName Value=""/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>

View File

@ -61,8 +61,7 @@ type
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); override;
procedure CreateNumFormatList; override;
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Byte;
out ACurrencySymbol: String; out ANumberFormatStr: String); override;
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String); override;
procedure ReadBlank(AStream: TStream); override;
procedure ReadColWidth(AStream: TStream);
procedure ReadFont(AStream: TStream);
@ -139,7 +138,7 @@ var
implementation
uses
Math;
Math, fpsNumFormatParser;
const
{ Excel record IDs }
@ -181,21 +180,23 @@ begin
ts := fs.ThousandSeparator;
cs := fs.CurrencyString;
AddFormat( 0, '', nfGeneral);
AddFormat( 1, '0', nfFixed, 0);
AddFormat( 2, '0'+ds+'00', nfFixed, 2); // 0.00
AddFormat( 3, '#'+ts+'##0', nfFixedTh, 0); // #,##0
AddFormat( 4, '#'+ts+'##0'+ds+'00', nfFixedTh, 2); // #,##0.00
AddFormat( 5, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);("'+cs+'"#'+ts+'##0)'), nfCurrency, 0);
AddFormat( 6, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);[Red]("'+cs+'"#'+ts+'##0)'), nfCurrencyRed, 2);
AddFormat( 7, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency, 0);
AddFormat( 8, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);[Red]("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency, 2);
AddFormat( 9, '0%', nfPercentage, 0);
AddFormat(10, '0'+ds+'00%', nfPercentage, 2);
AddFormat(11, '0'+ds+'00E+00', nfExp, 2);
AddFormat( 1, '0', nfFixed);
AddFormat( 2, '0'+ds+'00', nfFixed); // 0.00
AddFormat( 3, '#'+ts+'##0', nfFixedTh); // #,##0
AddFormat( 4, '#'+ts+'##0'+ds+'00', nfFixedTh); // #,##0.00
AddFormat( 5, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);("'+cs+'"#'+ts+'##0)'), nfCurrency);
AddFormat( 6, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);[Red]("'+cs+'"#'+ts+'##0)'), nfCurrencyRed);
AddFormat( 7, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency);
AddFormat( 8, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);[Red]("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0'+ds+'00%', nfPercentage);
AddFormat(11, '0'+ds+'00E+00', nfExp);
AddFormat(12, fs.ShortDateFormat, nfShortDate);
AddFormat(13, fs.LongDateFormat, nfLongDate);
AddFormat(14, SpecialDateTimeFormat('dm', fs, true), nfFmtDateTime);
AddFormat(15, SpecialDateTimeFormat('my', fs, true), nfFmtDateTime);
AddFormat(14, 'd/mmm', nfCustom);
AddFormat(15, 'mmm/yy', nfCustom);
//AddFormat(14, SpecialDateTimeFormat('dm', fs, true), nfFmtDateTime);
//AddFormat(15, SpecialDateTimeFormat('my', fs, true), nfFmtDateTime);
AddFormat(16, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM);
AddFormat(17, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM);
AddFormat(18, fs.ShortTimeFormat, nfShortTime);
@ -223,6 +224,7 @@ begin
if ADecimals > 0 then ADecimals := 2;
ANumFormat := nfExp;
end;
{
nfFmtDateTime:
begin
fmt := lowercase(AFormatString);
@ -248,6 +250,7 @@ begin
else
ANumFormat := nfShortDateTime;
end;
}
nfCustom, nfTimeInterval:
begin
ANumFormat := nfGeneral;
@ -261,18 +264,28 @@ end;
function TsBIFF2NumFormatList.FindFormatOf(AFormatCell: PCell): Integer;
var
fmt: String;
parser: TsNumFormatParser;
decs: Integer;
dt: string;
begin
parser := TsNumFormatParser.Create(Workbook, AFormatCell^.NumberFormatStr);
try
decs := parser.Decimals;
dt := parser.GetDateTimeCode(0);
finally
parser.Free;
end;
case AFormatCell^.NumberFormat of
nfGeneral,
nfCustom,
nfTimeInterval : Result := 0;
nfFixed : Result := IfThen(AFormatCell^.Decimals = 0, 1, 2);
nfFixedTh : Result := IfThen(AFormatCell^.Decimals = 0, 3, 4);
nfFixed : Result := IfThen(decs = 0, 1, 2);
nfFixedTh : Result := IfThen(decs = 0, 3, 4);
nfCurrency,
nfAccounting : Result := IfThen(AFormatCell^.Decimals = 0, 5, 7);
nfAccounting : Result := IfThen(decs = 0, 5, 7);
nfCurrencyRed,
nfAccountingRed : Result := IfThen(AFormatCell^.Decimals = 0, 6, 8);
nfPercentage : Result := IfThen(AFormatCell^.Decimals = 0, 9, 10);
nfAccountingRed : Result := IfThen(decs = 0, 6, 8);
nfPercentage : Result := IfThen(decs = 0, 9, 10);
nfExp, nfSci : Result := 11;
nfShortDate : Result := 12;
nfLongDate : Result := 13;
@ -281,38 +294,8 @@ begin
nfShortTime : Result := 18;
nfLongTime : Result := 19;
nfShortDateTime : Result := 20;
nfFmtDateTime : begin
fmt := lowercase(AFormatCell^.NumberFormatStr);
if (fmt = 'd-mmm') or (fmt = 'd/mmm') or
(fmt = 'd-mm') or (fmt = 'd/mm') or
(fmt = 'dd-mm') or (fmt = 'dd/mm') or
(fmt = 'dd-mmm') or (fmt = 'dd/mmm')
then
Result := 14
else
if (fmt = 'mmm-yy') or (fmt = 'mmm/yy') or
(fmt = 'mm-yy') or (fmt = 'mm/yy') or
(fmt = 'm-yy') or (fmt = 'm/y') or
(fmt = 'mmm-yyyy') or (fmt = 'mmm/yyyy') or
(fmt = 'mm-yyyy') or (fmt = 'mm/yyyy') or
(fmt = 'm-yyyy') or (fmt = 'm/yyyy')
then
Result := 15
else
if (fmt = 'nn:ss') or (fmt = 'mm:ss') or
(fmt = 'n:ss') or (fmt = 'm:ss')
then
Result := 19
else
if (fmt = 'nn:ss.z') or (fmt = 'mm:ss.z') or
(fmt = 'n:ss.z') or (fmt = 'm:ss.z') or
(fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or
(fmt = 'n:ss.zzz') or (fmt = 'm:ss.zzz')
then
Result := 19
else
Result := 20;
end;
nfCustom : if dt = 'dm' then Result := 14 else
if dt = 'my' then Result := 15;
end;
end;
@ -383,8 +366,7 @@ 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: Byte;
out ACurrencySymbol: String; out ANumberFormatStr: String);
out ANumberFormat: TsNumberFormat; out ANumberFormatStr: String);
var
lNumFormatData: TsNumFormatData;
begin
@ -392,13 +374,9 @@ begin
if lNumFormatData <> nil then begin
ANumberFormat := lNumFormatData.NumFormat;
ANumberFormatStr := lNumFormatData.FormatString;
ADecimals := lNumFormatData.Decimals;
ACurrencySymbol := lNumFormatData.CurrencySymbol;
end else begin
ANumberFormat := nfGeneral;
ANumberFormatStr := '';
ADecimals := 0;
ACurrencySymbol := '';
end;
end;
@ -575,11 +553,11 @@ begin
Move(Data[0], formulaResult, SizeOf(Data));
{Find out what cell type, set content type and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(formulaResult, nf, dt) then
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(formulaResult, nf, nfs, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else
FWorksheet.WriteNumber(ARow, ACol, formulaResult, nf, nd, ncs);
FWorksheet.WriteNumber(ARow, ACol, formulaResult, nf, nfs);
end;
{ Formula token array }
@ -645,11 +623,11 @@ begin
AStream.ReadBuffer(value, 8);
{Find out what cell type, set content type and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(value, nf, dt) then
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(value, nf, nfs, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else
FWorksheet.WriteNumber(ARow, ACol, value, nf, nd, ncs);
FWorksheet.WriteNumber(ARow, ACol, value, nf, nfs);
{ Apply formatting to cell }
ApplyCellFormatting(ARow, ACol, XF);
@ -902,6 +880,7 @@ begin
// but the number format list of the writer is in Excel syntax.
// And for BIFF2, there is only a limited number of formats.
lCell := ACell^;
{
with lCell do begin
if IsDateTimeFormat(NumberFormat) then
NumberFormatStr := BuildDateTimeFormatString(NumberFormat,
@ -911,6 +890,7 @@ begin
Workbook.FormatSettings, Decimals, CurrencySymbol);
NumFormatList.ConvertBeforeWriting(NumberFormatStr, NumberFormat, Decimals, CurrencyString);
end;
}
lIndex := FindFormattingInList(@lCell);
// Carefully check the index
@ -925,14 +905,13 @@ var
i: Integer;
begin
inherited ListAllFormattingStyles;
{
for i:=0 to High(FFormattingStyles) do
FNumFormatList.ConvertBeforeWriting(
FFormattingStyles[i].NumberFormatStr,
FFormattingStyles[i].NumberFormat,
FFormattingStyles[i].Decimals,
FFormattingStyles[i].CurrencySymbol
FFormattingStyles[i].NumberFormat
);
}
end;
{ Builds up the list of number formats to be written to the biff2 file.

View File

@ -310,6 +310,8 @@ begin
// Now apply the modifications.
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
// The number formats in the FormattingStyles are still in fpc dialect
// They will be converted to Excel syntax immediately before writing.
j := NumFormatList.FindFormatOf(@FFormattingStyles[i]);
if j > -1 then
lFormatIndex := NumFormatList[j].Index;

View File

@ -357,6 +357,9 @@ type
TsBIFFNumFormatList = class(TsCustomNumFormatList)
protected
procedure AddBuiltinFormats; override;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); override;
public
end;
@ -375,13 +378,15 @@ type
function DecodeRKValue(const ARK: DWORD): Double;
// Returns the numberformat for a given XF record
procedure ExtractNumberFormat(AXFIndex: WORD;
out ANumberFormat: TsNumberFormat; out ADecimals: Byte;
out ACurrencySymbol: String; out ANumberFormatStr: String); virtual;
out ANumberFormat: TsNumberFormat; //out ADecimals: Byte;
//out ACurrencySymbol: String;
out ANumberFormatStr: String); virtual;
// Finds format record for XF record pointed to by cell
// Will not return info for built-in formats
function FindNumFormatDataForCell(const AXFIndex: Integer): TsNumFormatData;
// Tries to find if a number cell is actually a date/datetime/time cell and retrieves the value
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): Boolean;
function IsDateTime(Number: Double; ANumberFormat: TsNumberFormat;
ANumberFormatStr: String; var ADateTime: TDateTime): Boolean;
// Here we can add reading of records which didn't change across BIFF5-8 versions
procedure ReadCodePage(AStream: TStream);
// Read column info
@ -708,48 +713,50 @@ end;
{ These are the built-in number formats as expected in the biff spreadsheet file.
In BIFF5+ they are not written to file but they are used for lookup of the
number format that Excel used. They have to be converted to fpspreadsheet format. }
number format that Excel used. They are specified here in fpc dialect. }
procedure TsBIFFNumFormatList.AddBuiltinFormats;
var
fs: TFormatSettings;
cs: String;
begin
fs := Workbook.FormatSettings;
cs := AnsiToUTF8(Workbook.FormatSettings.CurrencyString);
AddFormat( 0, '', nfGeneral);
AddFormat( 1, '0', nfFixed, 0);
AddFormat( 2, '0.00', nfFixed, 2);
AddFormat( 3, '#,##0', nfFixedTh, 0);
AddFormat( 4, '#,##0.00', nfFixedTh, 2);
AddFormat( 5, '"'+cs+'"#,##0_);("'+cs+'"#,##0)', nfCurrency, 0);
AddFormat( 6, '"'+cs+'"#,##0_);[Red]("'+cs+'"#,##0)', nfCurrencyRed, 0);
AddFormat( 7, '"'+cs+'"#,##0.00_);("'+cs+'"#,##0.00)', nfCurrency, 2);
AddFormat( 8, '"'+cs+'"#,##0.00_);[Red]("'+cs+'"#,##0.00)', nfCurrencyRed, 2);
AddFormat( 9, '0%', nfPercentage, 0);
AddFormat(10, '0.00%', nfPercentage, 2);
AddFormat(11, '0.00E+00', nfExp, 2);
AddFormat( 1, '0', nfFixed);
AddFormat( 2, '0.00', nfFixed);
AddFormat( 3, '#,##0', nfFixedTh);
AddFormat( 4, '#,##0.00', nfFixedTh);
AddFormat( 5, '"'+cs+'"#,##0_);("'+cs+'"#,##0)', nfCurrency);
AddFormat( 6, '"'+cs+'"#,##0_);[Red]("'+cs+'"#,##0)', nfCurrencyRed);
AddFormat( 7, '"'+cs+'"#,##0.00_);("'+cs+'"#,##0.00)', nfCurrency);
AddFormat( 8, '"'+cs+'"#,##0.00_);[Red]("'+cs+'"#,##0.00)', nfCurrencyRed);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0.00%', nfPercentage);
AddFormat(11, '0.00E+00', nfExp);
// fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported
AddFormat(14, 'M/D/YY', nfShortDate);
AddFormat(15, 'D-MMM-YY', nfLongDate);
AddFormat(16, 'D-MMM', nfFmtDateTime);
AddFormat(17, 'MMM-YY', nfFmtDateTime);
AddFormat(18, 'h:mm AM/PM', nfShortTimeAM);
AddFormat(19, 'h:mm:ss AM/PM', nfLongTimeAM);
AddFormat(20, 'h:mm', nfShortTime);
AddFormat(21, 'h:mm:ss', nfLongTime);
AddFormat(22, 'M/D/YY h:mm', nfShortDateTime);
AddFormat(14, fs.ShortDateFormat, nfShortDate); // 'M/D/YY'
AddFormat(15, fs.LongDateFormat, nfLongDate); // 'D-MMM-YY'
AddFormat(16, 'd/mmm', nfCustom); // 'D-MMM'
AddFormat(17, 'mmm/yy', nfCustom); // 'MMM-YY'
AddFormat(18, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM); // 'h:mm AM/PM'
AddFormat(19, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM); // 'h:mm:ss AM/PM'
AddFormat(20, fs.ShortTimeFormat, nfShortTime); // 'h:mm'
AddFormat(21, fs.LongTimeFormat, nfLongTime); // 'h:mm:ss'
AddFormat(22, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime); // 'M/D/YY h:mm' (localized)
// 23..36 not supported
AddFormat(37, '_(#,##0_);(#,##0)', nfCurrency, 0);
AddFormat(38, '_(#,##0_);[Red](#,##0)', nfCurrencyRed, 0);
AddFormat(39, '_(#,##0.00_);(#,##0.00)', nfCurrency, 2);
AddFormat(40, '_(#,##0.00_);[Red](#,##0.00)', nfCurrencyRed, 2);
AddFormat(41, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)', nfAccounting, 0);
AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfAccounting, 0);
AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfAccounting, 2);
AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfAccounting, 2);
AddFormat(45, 'mm:ss', nfFmtDateTime);
AddFormat(46, '[h]:mm:ss', nfTimeInterval);
AddFormat(47, 'mm:ss.0', nfFmtDateTime);
AddFormat(48, '##0.0E+00', nfSci, 1);
AddFormat(37, '_(#,##0_);(#,##0)', nfCurrency);
AddFormat(38, '_(#,##0_);[Red](#,##0)', nfCurrencyRed);
AddFormat(39, '_(#,##0.00_);(#,##0.00)', nfCurrency);
AddFormat(40, '_(#,##0.00_);[Red](#,##0.00)', nfCurrencyRed);
AddFormat(41, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)', nfAccounting);
AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfAccounting);
AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfAccounting);
AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfAccounting);
AddFormat(45, 'nn:ss', nfCustom);
AddFormat(46, '[h]:nn:ss', nfTimeInterval);
AddFormat(47, 'nn:ss.z', nfCustom);
AddFormat(48, '##0.0E+00', nfSci);
// 49 ("Text") not supported
// All indexes from 0 to 163 are reserved for built-in formats.
@ -758,6 +765,26 @@ begin
FNextFormatIndex := 164;
end;
procedure TsBIFFNumFormatList.ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String);
var
parser: TsNumFormatParser;
fmt: String;
begin
parser := TsNumFormatParser.Create(Workbook, AFormatString);
try
if parser.Status = psOK then begin
// We convert the fpc format string to Excel dialect
AFormatString := parser.FormatString[nfdExcel];
ANumFormat := parser.NumFormat;
ADecimals := parser.Decimals;
ACurrencySymbol := parser.CurrencySymbol;
end;
finally
parser.Free;
end;
end;
{ TsSpreadBIFFReader }
@ -840,12 +867,14 @@ var
begin
FreeAndNil(FNumFormatList);
FNumFormatList := TsBIFFNumFormatList.Create(Workbook);
(*
// Convert builtin formats to fps syntax
for i:=0 to FNumFormatList.Count-1 do begin
item := FNumFormatList[i];
FNumFormatList.ConvertAfterReading(item.Index, item.FormatString,
item.NumFormat, item.Decimals, item.CurrencySymbol);
end;
*)
end;
{ Extracts a number out of an RK value.
@ -883,8 +912,9 @@ 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;
out ANumberFormat: TsNumberFormat; out ADecimals: Byte;
out ACurrencySymbol: String; out ANumberFormatStr: String);
out ANumberFormat: TsNumberFormat; //out ADecimals: Byte;
//out ACurrencySymbol: String;
out ANumberFormatStr: String);
var
lNumFormatData: TsNumFormatData;
begin
@ -892,13 +922,13 @@ begin
if lNumFormatData <> nil then begin
ANumberFormat := lNumFormatData.NumFormat;
ANumberFormatStr := lNumFormatData.FormatString;
ADecimals := lNumFormatData.Decimals;
ACurrencySymbol := lNumFormatData.CurrencySymbol;
// ADecimals := lNumFormatData.Decimals;
// ACurrencySymbol := lNumFormatData.CurrencySymbol;
end else begin
ANumberFormat := nfGeneral;
ANumberFormatStr := '';
ADecimals := 0;
ACurrencySymbol := '';
// ADecimals := 0;
// ACurrencySymbol := '';
end;
end;
@ -918,22 +948,30 @@ end;
{ Convert the number to a date/time and return that if it is }
function TsSpreadBIFFReader.IsDateTime(Number: Double;
ANumberFormat: TsNumberFormat; var ADateTime: TDateTime): boolean;
ANumberFormat: TsNumberFormat; ANumberFormatStr: String;
var ADateTime: TDateTime): boolean;
var
parser: TsNumFormatParser;
begin
Result := true;
if ANumberFormat in [
nfShortDateTime, nfFmtDateTime, nfShortDate, nfLongDate,
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM] then
begin
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode);
Result := true;
end else
if ANumberFormat = nfTimeInterval then begin
ADateTime := Number;
Result := true;
end else
begin
ADateTime := 0;
Result := false;
nfShortDateTime, {nfFmtDateTime, }nfShortDate, nfLongDate,
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM]
then
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode)
else
if ANumberFormat = nfTimeInterval then
ADateTime := Number
else begin
parser := TsNumFormatParser.Create(Workbook, ANumberFormatStr);
try
if (parser.Status = psOK) and parser.IsDateTimeFormat then
ADateTime := ConvertExcelDateTimeToDateTime(Number, FDateMode)
else
Result := false;
finally
parser.Free;
end;
end;
end;
@ -1108,11 +1146,11 @@ begin
Move(Data[0], ResultFormula, SizeOf(Data));
{Find out what cell type, set content type and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(ResultFormula, nf, dt) then
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(ResultFormula, nf, nfs, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else
FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nd, ncs);
FWorksheet.WriteNumber(ARow, ACol, ResultFormula, nf, nfs); //, nd, ncs);
end;
{ Formula token array }
@ -1174,11 +1212,11 @@ begin
RK := DWordLEtoN(AStream.ReadDWord);
lNumber := DecodeRKValue(RK);
{Find out what cell type, set contenttype and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(lNumber, nf, lDateTime) then
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(lNumber, nf, nfs, lDateTime) then
FWorksheet.WriteDateTime(ARow, fc, lDateTime, nf, nfs)
else
FWorksheet.WriteNumber(ARow, fc, lNumber, nf, nd, ncs);
FWorksheet.WriteNumber(ARow, fc, lNumber, nf, nfs);
inc(fc);
dec(pending, SizeOf(XF) + SizeOf(RK));
end;
@ -1210,13 +1248,11 @@ begin
AStream.ReadBuffer(value, 8);
{Find out what cell type, set content type and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(value, nf, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt) //, nf, nfs)
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(value, nf, nfs, dt) then
FWorksheet.WriteDateTime(ARow, ACol, dt, nf, nfs)
else
//if nf <> nfCustom then // why was this here?
FWorksheet.WriteNumber(ARow, ACol, value, nf, nd, ncs);
FWorksheet.WriteNumberFormat(ARow, ACol, nf, nfs); // override built-in format string
FWorksheet.WriteNumber(ARow, ACol, value, nf, nfs);
{ Add attributes to cell }
ApplyCellFormatting(ARow, ACol, XF);
@ -1297,17 +1333,17 @@ begin
Number := DecodeRKValue(RK);
{Find out what cell type, set contenttype and value}
ExtractNumberFormat(XF, nf, nd, ncs, nfs);
if IsDateTime(Number, nf, lDateTime) then
ExtractNumberFormat(XF, nf, nfs);
if IsDateTime(Number, nf, nfs, lDateTime) then
FWorksheet.WriteDateTime(ARow, ACol, lDateTime, nf, nfs)
else
FWorksheet.WriteNumber(ARow, ACol, Number, nf, nd, ncs);
FWorksheet.WriteNumber(ARow, ACol, Number, nf, nfs);
{Add attributes}
ApplyCellFormatting(ARow, ACol, XF);
end;
// Read the part of the ROW record that is common to all BIFF versions
// Read the part of the ROW record that is common to BIFF3-8 versions
procedure TsSpreadBIFFReader.ReadRowInfo(AStream: TStream);
type
TRowRecord = packed record
@ -1325,6 +1361,21 @@ var
h: word;
begin
AStream.ReadBuffer(rowrec, SizeOf(TRowRecord));
// if bit 6 is set in the flags row height does not match the font size.
// Only for this case we create a row record for fpspreadsheet
if rowrec.Flags and $00000040 <> 0 then begin
lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex));
// row height is encoded into the 15 lower bits in units "twips" (1/20 pt)
// we need it in "lines", i.e. we divide the points by the point size of the default font
h := WordLEToN(rowrec.Height) and $7FFF;
lRow^.Height := TwipsToPts(h) / FWorkbook.GetDefaultFontSize;
if lRow^.Height > ROW_HEIGHT_CORRECTION then
lRow^.Height := lRow^.Height - ROW_HEIGHT_CORRECTION
else
lRow^.Height := 0;
end;
{
h := WordLEToN(rowrec.Height);
if h and $8000 = 0 then begin // if this bit were set, rowheight would be default
lRow := FWorksheet.GetRow(WordLEToN(rowrec.RowIndex));
@ -1336,6 +1387,7 @@ begin
else
lRow^.Height := 0;
end;
}
end;
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
@ -2118,6 +2170,7 @@ begin
// But we have to consider that the number formats of the cell is in fpc syntax,
// but the number format list of the writer is in Excel syntax.
lCell := ACell^;
(*
with lCell do begin
if NumberFormat <> nfCustom then begin
if IsDateTimeFormat(NumberFormat) then
@ -2126,9 +2179,10 @@ begin
else
NumberFormatStr := BuildNumberFormatString(NumberFormat,
Workbook.FormatSettings, Decimals, CurrencySymbol);
NumFormatList.ConvertBeforeWriting(NumberFormatStr, NumberFormat, Decimals, CurrencyString);
//NumFormatList.ConvertBeforeWriting(NumberFormatStr, NumberFormat, Decimals, CurrencyString);
end;
end;
*)
lIndex := FindFormattingInList(@lCell);
// Carefully check the index