fpspreadsheet: Fix issues related to biff2 expecting localized formatting strings.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3164 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-06-13 13:33:02 +00:00
parent 4d2ea06bfa
commit 36765d4f9b
5 changed files with 149 additions and 90 deletions

View File

@ -45,6 +45,8 @@ begin
}
// Write some number cells
MyWorksheet.WriteNumber(0, 0, 0.0, nfFixed, 2);
(*
MyWorksheet.WriteNumber(0, 0, 1.0);
MyWorksheet.WriteUsedFormatting(0, 0, [uffBold, uffNumberFormat]);
MyWorksheet.WriteNumber(0, 1, 2.0);
@ -138,11 +140,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);
@ -150,16 +152,16 @@ 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, nn:ss');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'nn:ss');
MyWorksheet.WriteFontColor(r, 1, scGray);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfFmtDateTime, MSZ');
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'MSZ');
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, nn:ss.z');
MyWorksheet.WriteDateTime(r, 1, now, nfCustom, 'nn:ss.z');
MyWorksheet.WriteFontColor(r, 1, scGray);
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');
MyWorksheet.WriteFontColor(r, 1, scGray);
// Write formatted numbers
@ -269,24 +271,24 @@ begin
MyWorksheet.WriteFontColor(r, 4, scGray);
inc(r,2);
MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs');
MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, '$');
MyWorksheet.WriteNumber(r, 2, -number, nfCurrency, 0, '$');
MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrency, 0, '$');
MyWorksheet.WriteCurrency(r, 1, number, nfCurrency, 0, '$');
MyWorksheet.WriteCurrency(r, 2, -number, nfCurrency, 0, '$');
MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrency, 0, '$');
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, "$"#,##0_);("$"#,##0)');
MyWorksheet.WriteNumber(r, 1, number);
@ -378,7 +380,7 @@ begin
MyWorksheet.WriteRowInfo(5, lRow);
lRow.Height := 2; // 2 lines
MyWorksheet.WriteRowInfo(6, lRow);
*)
// Save the spreadsheet to a file
MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel2, true);
MyWorkbook.Free;

View File

@ -155,6 +155,8 @@ type
*)
function GetDateTimeCode(ASection: Integer): String;
function IsDateTimeFormat: Boolean;
procedure LimitDecimals;
procedure Localize;
property CurrencySymbol: String read GetCurrencySymbol;
property Decimals: Byte read GetDecimals write SetDecimals;
@ -358,10 +360,8 @@ begin
case element.Token of
nftText:
if element.TextValue <> '' then result := Result + '"' + element.TextValue + '"';
nftThSep:
Result := Result + ',';
nftDecSep:
Result := Result + '.';
nftThSep, nftDecSep:
Result := Result + element.TextValue;
nftDigit:
Result := Result + '0';
nftOptDigit, nftOptDec:
@ -812,6 +812,7 @@ begin
nftDay : Result := Result + 'd';
nftHour : Result := Result + 'h';
nftMinute : Result := Result + 'n';
nftSecond : Result := Result + 's';
nftMilliSeconds: Result := Result + 'z';
end;
inc(i);
@ -1274,6 +1275,40 @@ begin
(FSections[ASection].Elements[AIndex].Token = AToken);
end;
{ Limits the decimals to 0 or 2, as required by Excel }
procedure TsNumFormatParser.LimitDecimals;
var
i, j: Integer;
begin
for j:=0 to High(FSections) do
for i:=0 to High(FSections[j].Elements) do
if FSections[j].Elements[i].Token = nftDecs then
if FSections[j].Elements[i].IntValue > 0 then
FSections[j].Elements[i].IntValue := 2;
end;
{ Localizes the thousand- and decimal separator symbols by replacing them with
the FormatSettings value of the workbook. A recreated format string will be
localized as required by Excel2 }
procedure TsNumFormatParser.Localize;
var
i, j: Integer;
fs: TFormatSettings;
txt: String;
begin
fs := FWorkbook.FormatSettings;
for j:=0 to High(FSections) do
for i:=0 to High(FSections[j].Elements) do begin
txt := FSections[j].Elements[i].TextValue;
case FSections[j].Elements[i].Token of
nftThSep : txt := fs.ThousandSeparator;
nftDecSep : txt := fs.DecimalSeparator;
nftCurrSymbol: txt := UTF8ToAnsi(txt);
end;
FSections[j].Elements[i].TextValue := txt;
end;
end;
function TsNumFormatParser.NextToken: Char;
var
delta: Integer;

View File

@ -700,8 +700,7 @@ type
procedure ConvertAfterReading(AFormatIndex: Integer; var AFormatString: String;
var ANumFormat: TsNumberFormat); virtual;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); virtual;
var ANumFormat: TsNumberFormat); virtual;
procedure Delete(AIndex: Integer);
function Find(ANumFormat: TsNumberFormat; AFormatString: String): Integer; overload;
function Find(AFormatString: String): Integer; overload;
@ -3479,7 +3478,7 @@ end;
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 ANumFormat: TsNumberFormat);
begin
// nothing to do here. But see, e.g., xlscommon.TsBIFFNumFormatList
end;
@ -3600,13 +3599,12 @@ function TsCustomNumFormatList.FormatStringForWriting(AIndex: Integer): String;
var
item: TsNumFormatdata;
nf: TsNumberFormat;
decs: Byte;
cs: String;
begin
item := Items[AIndex];
if item <> nil then begin
Result := item.FormatString;
ConvertBeforeWriting(Result, nf, decs, cs);
nf := item.NumFormat;
ConvertBeforeWriting(Result, nf);
end else
Result := '';
end;
@ -3652,7 +3650,7 @@ begin
inherited Create;
FWorkbook := AWorkbook;
CreateNumFormatList;
FNumFormatList.FWorkbook := AWorkbook;
// FNumFormatList.FWorkbook := AWorkbook;
end;
destructor TsCustomSpreadReader.Destroy;
@ -3723,7 +3721,7 @@ begin
inherited Create;
FWorkbook := AWorkbook;
CreateNumFormatList;
FNumFormatList.FWorkbook := AWorkbook;
// FNumFormatList.FWorkbook := AWorkbook;
end;
destructor TsCustomSpreadWriter.Destroy;

View File

@ -43,7 +43,7 @@ type
protected
procedure AddBuiltinFormats; override;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String); override;
var ANumFormat: TsNumberFormat); override;
function FindFormatOf(AFormatCell: PCell): Integer; override;
public
constructor Create(AWorkbook: TsWorkbook);
@ -57,6 +57,7 @@ type
WorkBookEncoding: TsEncoding;
FWorksheet: TsWorksheet;
FFont: TsFont;
FFmtIndex: Integer;
protected
procedure ApplyCellFormatting(ARow, ACol: Cardinal; XFIndex: Word); override;
procedure CreateNumFormatList; override;
@ -66,6 +67,7 @@ type
procedure ReadColWidth(AStream: TStream);
procedure ReadFont(AStream: TStream);
procedure ReadFontColor(AStream: TStream);
procedure ReadFormat(AStream: TStream); override;
procedure ReadFormula(AStream: TStream); override;
procedure ReadInteger(AStream: TStream);
procedure ReadLabel(AStream: TStream); override;
@ -170,11 +172,41 @@ begin
inherited Create(AWorkbook);
end;
{ Prepares the list of built-in number formats. They are created in the default
dialect for FPC, they have to be converted to Excel syntax before writing.
Note that Excel2 expects them to be localized. This is something which has to
be taken account of in ConverBeforeWriting.}
procedure TsBIFF2NumFormatList.AddBuiltinFormats;
var
fs: TFormatSettings;
ds, ts, cs: string;
begin
fs := FWorkbook.FormatSettings;
AddFormat( 0, '', nfGeneral);
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)', nfCurrency);
AddFormat( 9, '0%', nfPercentage);
AddFormat(10, '0.00%', nfPercentage);
AddFormat(11, '0.00E+00', nfExp);
AddFormat(12, fs.ShortDateFormat, nfShortDate);
AddFormat(13, fs.LongDateFormat, nfLongDate);
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);
AddFormat(19, fs.LongTimeFormat, nfLongTime);
AddFormat(20, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime);
(*
fs := Workbook.FormatSettings;
ds := fs.DecimalSeparator;
ts := fs.ThousandSeparator;
@ -202,61 +234,29 @@ begin
AddFormat(18, fs.ShortTimeFormat, nfShortTime);
AddFormat(19, fs.LongTimeFormat, nfLongTime);
AddFormat(20, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime);
*)
FFirstFormatIndexInFile := 0; // BIFF2 stores built-in formats to file.
FNextFormatIndex := 21; // not needed - there are not user-defined formats
end;
procedure TsBIFF2NumFormatList.ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String);
var ANumFormat: TsNumberFormat);
var
fmt: String;
parser: TsNumFormatParser;
begin
case ANumFormat of
nfGeneral:
;
nfFixed, nfFixedTh, nfPercentage, nfExp,
nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed:
if ADecimals > 0 then ADecimals := 2;
nfSci:
begin
if ADecimals > 0 then ADecimals := 2;
ANumFormat := nfExp;
end;
{
nfFmtDateTime:
begin
fmt := lowercase(AFormatString);
if (fmt = 'd-mm') or (fmt = 'd/mm') or
(fmt = 'dd-mm') or (fmt = 'dd/mm') or
(fmt = 'dd-mmm') or (fmt = 'dd/mmm')
then
AFormatString := SpecialDateTimeFormat('dm', Workbook.FormatSettings, true)
else
if (fmt = 'm-yy') or (fmt = 'm/yy') or
(fmt = 'mm-yy') or (fmt = 'mm/yy') or
(fmt = 'mmm-yy') or (fmt = 'mmm/yy') or
(fmt = 'm-yyyy') or (fmt = 'm/yyyy') or
(fmt = 'mm-yyyy') or (fmt = 'mm/yyyy') or
(fmt = 'mmm-yyyy') or (fmt = 'mmm-yyyy')
then
AFormatString := SpecialDateTimeFormat('my', Workbook.FormatSettings, true)
else
if (copy(fmt, 1, 5) = 'nn:ss') or (copy(fmt, 1, 5) = 'mm:ss') or
(copy(fmt, 1, 4) = 'n:ss') or (copy(fmt, 1, 4) = 'm:ss')
then
ANumFormat := nfLongTime
else
ANumFormat := nfShortDateTime;
end;
}
nfCustom, nfTimeInterval:
begin
ANumFormat := nfGeneral;
AFormatString := '';
ADecimals := 0;
end;
if AFormatString = '' then
AFormatString := 'General'
else begin
parser := TsNumFormatParser.Create(FWorkbook, AFormatString);
try
parser.Localize;
parser.LimitDecimals;
AFormatString := parser.FormatString[nfdExcel];
finally
parser.Free;
end;
end;
end;
@ -276,9 +276,8 @@ begin
parser.Free;
end;
Result := 0;
case AFormatCell^.NumberFormat of
nfGeneral,
nfTimeInterval : Result := 0;
nfFixed : Result := IfThen(decs = 0, 1, 2);
nfFixedTh : Result := IfThen(decs = 0, 3, 4);
nfCurrency,
@ -302,11 +301,21 @@ end;
{ Creates formatting strings that are written into the file. These are the
strings in the format list. The only exception is the nfGeneral entry which
is written as "General". }
function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String;
function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String;
begin
Result := inherited FormatStringForWriting(AIndex);
{
if Result = '' then
Result := 'General';
Result := 'General'
else begin
parser := TsNumFormatParser.Create(Workbook, Result);
try
parser.Localize;
Result := Parser.FormatString[nfdExcel];
finally
end;
end;
}
end;
@ -445,6 +454,13 @@ begin
FFont.Color := WordLEToN(AStream.ReadWord);
end;
// Read the FORMAT record for formatting numerical data
procedure TsSpreadBIFF2Reader.ReadFormat(AStream: TStream);
begin
// We ignore the formats in the file, everything is known
// (Using the formats in the file would require de-localizing them).
end;
procedure TsSpreadBIFF2Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var
BIFF2EOF: Boolean;
@ -475,6 +491,7 @@ begin
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
INT_EXCEL_ID_FONT : ReadFont(AStream);
INT_EXCEL_ID_FONTCOLOR : ReadFontColor(AStream);
INT_EXCEL_ID_FORMAT : ReadFormat(AStream);
INT_EXCEL_ID_INTEGER : ReadInteger(AStream);
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
@ -917,7 +934,12 @@ end;
{ Builds up the list of number formats to be written to the biff2 file.
Unlike biff5+ no formats are added here because biff2 supports only 21
standard formats; these formats have been added by the NumFormatList's
AddBuiltInFormats. }
AddBuiltInFormats.
NOT CLEAR IF THIS IS TRUE ????
}
// ToDo: check if the BIFF2 format is really restricted to 21 formats.
procedure TsSpreadBIFF2Writer.ListAllNumFormats;
begin
// Nothing to do here.

View File

@ -358,8 +358,7 @@ type
protected
procedure AddBuiltinFormats; override;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); override;
var ANumFormat: TsNumberFormat); override;
public
end;
@ -766,7 +765,8 @@ begin
end;
procedure TsBIFFNumFormatList.ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String);
var ANumFormat: TsNumberFormat);
{var ADecimals: Byte; var ACurrencySymbol: String); }
var
parser: TsNumFormatParser;
fmt: String;
@ -777,8 +777,10 @@ 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;