fpspreadsheet: More logical writing strategy for biff number formats. Still issues with reading.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3072 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-05-21 12:12:16 +00:00
parent b9df429d5b
commit a9df011859
6 changed files with 471 additions and 182 deletions

View File

@ -54,7 +54,7 @@ begin
}
// Write some cells
MyWorksheet.WriteDateTime(0, 20, now, nfShortTime); //1.0);// A1
// MyWorksheet.WriteDateTime(0, 20, now, nfShortTime); //1.0);// A1
MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1
MyWorksheet.WriteNumber(0, 1, 2.0);// B1
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
@ -209,11 +209,9 @@ begin
MyWorksheet.WriteDateTime(r, 1, now, nfFmtDateTime, 'mm:ss.zzz');
// Write formatted numbers
number := 12345.67890123456789;
// number := 12345.67890123456789;
number := 31415.92;
inc(r, 2);
MyWorksheet.WriteUTF8Text(r, 1, '12345.67890123456789');
MyWorksheet.WriteUTF8Text(r, 2, '-12345.67890123456789');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfGeneral');
MyWorksheet.WriteNumber(r, 1, number, nfGeneral);
MyWorksheet.WriteNumber(r, 2, -number, nfGeneral);
@ -250,6 +248,12 @@ begin
MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3);
MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3);
inc(r,2);
MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 0 dec');
MyWorksheet.WriteNumber(r, 1, number, nfSci, 0);
MyWorksheet.WriteNumber(r, 2, -number, nfSci, 0);
MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 0);
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 0);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec');
MyWorksheet.WriteNumber(r, 1, number, nfSci, 1);
MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1);
@ -268,6 +272,12 @@ begin
MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3);
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 0 dec');
MyWorksheet.WriteNumber(r, 1, number, nfExp, 0);
MyWorksheet.WriteNumber(r, 2, -number, nfExp, 0);
MyWorksheet.WriteNumber(r, 3, 1.0/number, nfExp, 0);
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 0);
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec');
MyWorksheet.WriteNumber(r, 1, number, nfExp, 1);
MyWorksheet.WriteNumber(r, 2, -number, nfExp, 1);
@ -285,7 +295,6 @@ begin
MyWorksheet.WriteNumber(r, 2, -number, nfExp, 3);
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');
@ -319,6 +328,7 @@ begin
MyWorksheet.WriteNumberFormat(r, 1, nfCustom, '"$"#,##0.0_);[Red]("$"#,##0.0)');
MyWorksheet.WriteDateTime(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');
@ -337,14 +347,23 @@ begin
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');
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');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:mm');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:mm');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, hh:nn');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'hh:nn');
inc(r);
MyWorksheet.WriteUTF8Text(r, 0, 'nfTimeInterval, h:m');
MyWorksheet.WriteDateTime(r, 1, number, nfTimeInterval, 'h:m');
inc(r);
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');
@ -360,7 +379,7 @@ begin
// Set height of rows 0
MyWorksheet.WriteRowHeight(0, 30); // 30 mm
(*
// Creates a new worksheet
MyWorksheet := MyWorkbook.AddWorksheet(Str_Worksheet2);
@ -371,7 +390,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

@ -55,6 +55,7 @@ type
FFormatString: String;
FNumFormat: TsNumberFormat;
FConversionDirection: TsConversionDirection;
FIsTime: Boolean;
FStatus: Integer;
function GetFormatString: String;
function GetParsedSectionCount: Integer;
@ -78,7 +79,8 @@ type
procedure ScanText;
public
constructor Create(AWorkbook: TsWorkbook; const AFormatString: String;
constructor Create(AWorkbook: TsWorkbook;
const AFormatString: String; ANumFormat: TsNumberFormat;
AConversionDirection: TsConversionDirection = cdToFPSpreadsheet); overload;
constructor Create(AWorkbook: TsWorkbook; const AFormatSections: TsNumFormatSections;
AConversionDirection: TsConversionDirection = cdFromFPSpreadsheet); overload;
@ -96,7 +98,7 @@ type
implementation
uses
fpsutils;
StrUtils, fpsutils;
const
COMPARE_STR: array[TsCompareOperation] of string = (
@ -109,12 +111,14 @@ const
from a spreadsheet file. The conversion, by default, will go FROM the file TO
the fpspreadsheet procedures. }
constructor TsNumFormatParser.Create(AWorkbook: TsWorkbook;
const AFormatString: String; AConversionDirection: TsConversionDirection = cdToFPSpreadsheet);
const AFormatString: String; ANumFormat: TsNumberFormat;
AConversionDirection: TsConversionDirection = cdToFPSpreadsheet);
begin
inherited Create;
FCreateMethod := 0;
FConversionDirection := AConversionDirection;
FWorkbook := AWorkbook;
FNumFormat := ANumFormat;
FFormatSettings := DefaultFormatSettings;
FFormatSettings.DecimalSeparator := '.';
FFormatSettings.ThousandSeparator := ',';
@ -163,6 +167,7 @@ begin
Decimals := 0;
NumFormat := nfGeneral;
end;
FIsTime := false;
end;
procedure TsNumFormatParser.AnalyzeBracket(const AValue: String);
@ -171,6 +176,23 @@ var
n: Integer;
begin
lValue := lowercase(AValue);
// date/time format for interval
if (lValue = 'h') or (lValue = 'hh') or (lValue = 'm') or (lValue = 'mm') or
(lValue = 's') or (lValue = 'ss') or (lValue = 'n') or (lValue = 'nn')
then begin
FSections[FCurrSection].FormatString := FSections[FCurrSection].FormatString +
'[' + AValue + ']';
FSections[FCurrSection].NumFormat := nfTimeInterval;
FIsTime := true;
end
else
if ((lValue = 'n') or (lValue = 'nn')) and (FConversionDirection = cdFromFPSpreadsheet)
then begin
FSections[FCurrSection].FormatString := FSections[FCurrSection].FormatString +
'[' + DupeString('m', Length(lValue)) + ']';
FIsTime := true;
end
else
// Colors
if lValue = 'red' then
FSections[FCurrSection].Color := scRed
@ -251,7 +273,7 @@ begin
if FSections[i].FormatString = '' then
FSections[i].NumFormat := nfGeneral;
if (FSections[i].CurrencySymbol <> '') and (FSections[i].NumFormat = nfFixedTh) then
if (FSections[i].CurrencySymbol <> '') {and (FSections[i].NumFormat in [nfFixed, nfFixedTh])} then
FSections[i].NumFormat := nfCurrency;
if FSections[i].CompareOperation <> coNotUsed then begin
@ -270,7 +292,7 @@ begin
end;
nfShortDateTime, nfShortDate, nfShortTime, nfShortTimeAM,
nfLongDate, nfLongTime, nfLongTimeAM, nfFmtDateTime:
nfLongDate, nfLongTime, nfLongTimeAM, nfTimeInterval, nfFmtDateTime:
try
s := FormatDateTimeEx(FSections[i].FormatString, now(), FWorkbook.FormatSettings);
except
@ -280,6 +302,9 @@ begin
end;
end;
if (ns > 1) and (FNumFormat in [nfCurrencyRed, nfCurrencyDashRed]) then
FSections[1].Color := scRed;
// Extract built-in NumFormat identifier for currency (needs several entries in
// three sections).
if (ns = 3) and
@ -303,6 +328,22 @@ begin
else
FNumFormat := FSections[0].NumFormat;
// Add colors to section format strings
if (FConversionDirection = cdFromFPSpreadsheet) then
for i := 0 to High(FSections) do
if FSections[i].Color < 8 then
FSections[i].FormatString := Format('[%s]%s', [
FWorkbook.GetColorName(FSections[i].Color),
FSections[i].FormatString
])
else
if FSections[i].Color <> scNotDefined then
FSections[i].FormatString := Format('[Color%d]%s', [
FSections[i].Color,
FSections[i].FormatString
]);
// Construct total format string
if ns = 2 then
FFormatString := Format('%s;%s;%s', [
FSections[0].FormatString,
@ -408,6 +449,9 @@ var
begin
FStatus := psOK;
AddSection;
if AFormatString = '' then
exit;
FStart := @AFormatString[1];
FEnd := FStart + Length(AFormatString) - 1;
FCurrent := FStart;
@ -415,6 +459,7 @@ begin
token := FCurrent^;
case token of
'[': ScanBrackets;
':': if FIsTime then AddChar(':');
';': AddSection;
else ScanFormat;
end;
@ -444,6 +489,8 @@ begin
end;
end;
{ Scans a date/time format. Note: fpc and the Excel-standard have slightly
different formats which are converted here }
procedure TsNumFormatParser.ScanDateTime;
var
token: Char;
@ -452,55 +499,76 @@ var
i: Integer;
nf: TsNumberFormat;
partStr: String;
isTime: Boolean;
isAMPM: Boolean;
begin
done := false;
s := '';
isTime := false;
isAMPM := false;
while (FCurrent <= FEnd) and (FStatus = psOK) and (not done) do begin
token := FCurrent^;
case token of
'\':
'\': // means that the next character is taken literally
begin
inc(FCurrent);
token := FCurrent^;
inc(FCurrent); // skip the "\"...
token := FCurrent^; // and take the next character
s := s + token;
end;
'Y', 'y':
begin
ScanDateTimeParts(token, token, s);
isTime := false;
FIsTime := false;
end;
'M':
if FConversionDirection = cdToFPSpreadsheet then
ScanDateTimeParts(token, 'm', s)
else begin
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
end;
'm':
if FConversionDirection = cdToFPSpreadsheet then begin
if FIsTime then ScanDateTimeParts(token, 'n', s) else ScanDateTimeParts(token, 'm', s)
end else begin
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
end;
'M', 'm':
ScanDateTimeParts(token, token, s);
{if isTime then // help fpc to separate "month" and "minute"
ScanDateTimeParts(token, 'n', s)
else // both "month" and "minute" work in fpc to some degree
ScanDateTimeParts(token, token, s);}
'N', 'n':
ScanDateTimeParts(token, 'n', s); // fpc dialect for "minutes"
if FConversionDirection = cdToFPSpreadsheet then begin
// "n" is not used by file format --> stop scanning date/time
done := true;
dec(FCurrent);
end else
// "n", in fpc, stands for "minute".
ScanDateTimeParts(token, 'm', s);
'D', 'd':
begin
ScanDateTimeParts(token, token, s);
isTime := false;
FIsTime := false;
end;
'H', 'h':
begin
ScanDateTimeParts(token, token, s);
isTime := true;
FIsTime := true;
end;
'S', 's':
begin
ScanDateTimeParts(token, token, s);
isTime := true;
FIsTime := true;
end;
'/', ':', '.', ']', '[', ' ':
s := s + token;
'0', 'z', 'Z':
ScanDateTimeParts(token, token, s);
'0':
if FConversionDirection = cdToFPSpreadsheet then
ScanDateTimeParts(token, 'z', s)
else begin
done := true;
dec(FCurrent);
end;
'z', 'Z':
if FConversionDirection = cdToFPSpreadsheet then begin
done := true;
dec(FCurrent);
end else
ScanDateTimeParts(token, '0', s);
'A', 'a':
begin
ScanAMPM(s);
@ -530,6 +598,9 @@ begin
else
if s = StripAMPM(FWorkbook.FormatSettings.ShortTimeFormat) then
nf := IfThen(isAMPM, nfShortTimeAM, nfShortTime)
else
if s[1] = '[' then
nf := nfTimeInterval
else
nf := nfFmtDateTime;
@ -590,6 +661,10 @@ begin
inc(FCurrent);
ScanText;
end;
'(', ')':
begin
AddChar(token);
end;
'0', '#', '.', ',', '-':
ScanNumber;
'y', 'Y', 'm', 'M', 'd', 'D', 'h', 'N', 'n', 's', '[':

View File

@ -379,7 +379,8 @@ type
{ Data manipulation methods - For Cells }
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal);
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;
@ -561,24 +562,30 @@ type
FFirstFormatIndexInFile: Integer;
FNextFormatIndex: Integer;
procedure AddBuiltinFormats; virtual;
procedure Analyze(AFormatIndex: Integer; var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: byte;
var ACurrencySymbol: String); virtual;
procedure RemoveFormat(AIndex: Integer);
public
constructor Create(AWorkbook: TsWorkbook);
destructor Destroy; override;
function AddFormat(AFormatCell: PCell): Integer; overload;
function AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat;
AFormatString: String = ''; ADecimals: Byte = 0;
function AddFormat(AFormatIndex: Integer; AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer; overload;
function AddFormat(AFormatString: String; ANumFormat: TsNumberFormat;
ADecimals: Byte = 0; ACurrencySymbol: String = ''): 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;
procedure ConvertBeforeWriting(var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); virtual;
procedure Delete(AIndex: Integer);
function Find(AFormatCell: PCell): integer; overload;
function Find(ANumFormat: TsNumberFormat; AFormatString: String;
ADecimals: Byte; ACurrencySymbol: String): Integer; overload;
function Find(AFormatIndex: Integer): Integer; overload;
function Find(AFormatString: String): Integer; overload;
function FormatStringForWriting(AIndex: Integer): String; virtual;
procedure Sort;
@ -731,6 +738,7 @@ procedure RegisterSpreadFormat(
AWriterClass: TsSpreadWriterClass;
AFormat: TsSpreadsheetFormat);
procedure CopyCellFormat(AFromCell, AToCell: PCell);
function GetFileFormatName(AFormat: TsSpreadsheetFormat): String;
procedure MakeLEPalette(APalette: PsPalette; APaletteSize: Integer);
@ -749,6 +757,7 @@ resourcestring
lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
lpNoValidNumberFormatString = 'No valid number format string.';
lpNoValidDateTimeFormatString = 'No valid date/time format string.';
lpIllegalNumberFormat = 'Illegal number format.';
lpTRUE = 'TRUE';
lpFALSE = 'FALSE';
lpErrEmptyIntersection = '#NULL!';
@ -875,6 +884,28 @@ begin
{$ENDIF}
end;
{@@
Copies the format of a cell to another one.
}
procedure CopyCellFormat(AFromCell, AToCell: PCell);
begin
Assert(AFromCell <> nil);
Assert(AToCell <> nil);
AToCell^.UsedFormattingFields := AFromCell^.UsedFormattingFields;
AToCell^.BackgroundColor := AFromCell^.BackgroundColor;
AToCell^.Border := AFromCell^.Border;
AToCell^.BorderStyles := AFromCell^.BorderStyles;
AToCell^.FontIndex := AFromCell^.FontIndex;
AToCell^.HorAlignment := AFromCell^.HorAlignment;
AToCell^.VertAlignment := AFromCell^.VertAlignment;
AToCell^.TextRotation := AFromCell^.TextRotation;
AToCell^.NumberFormat := AFromCell^.NumberFormat;
AToCell^.NumberFormatStr := AFromCell^.NumberFormatStr;
AToCell^.Decimals := AFromCell^.Decimals;
AToCell^.CurrencySymbol := AFromCell^.CurrencySymbol;
end;
{ TsWorksheet }
@ -997,29 +1028,19 @@ end;
{@@
Copies all format parameters from the format cell to another cell.
}
procedure TsWorksheet.CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal);
var
cell: PCell;
procedure TsWorksheet.CopyFormat(AFromCell, AToCell: PCell);
begin
if AFormat = nil then
if (AFromCell = nil) or (AToCell = nil) then
exit;
cell := GetCell(AToRow, AToCol);
cell^.UsedFormattingFields := AFormat^.UsedFormattingFields;
cell^.BackgroundColor := AFormat^.BackgroundColor;
cell^.Border := AFormat^.Border;
cell^.BorderStyles := AFormat^.BorderStyles;
cell^.FontIndex := AFormat^.FontIndex;
cell^.HorAlignment := AFormat^.HorAlignment;
cell^.VertAlignment := AFormat^.VertAlignment;
cell^.TextRotation := AFormat^.TextRotation;
cell^.NumberFormat := AFormat^.NumberFormat;
cell^.NumberFormatStr := AFormat^.NumberFormatStr;
cell^.Decimals := AFormat^.Decimals;
cell^.CurrencySymbol := AFormat^.CurrencySymbol;
CopyCellFormat(AFromCell, AToCell);
ChangedCell(AToCell^.Row, AToCell^.Col);
ChangedFont(AToCell^.Row, AToCell^.Col);
end;
ChangedCell(AToRow, AToCol);
ChangedFont(AToRow, AToCol);
procedure TsWorksheet.CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal);
begin
CopyFormat(AFormat, GetCell(AToRow, AToCol));
end;
{@@
@ -1433,6 +1454,9 @@ begin
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;
@ -1457,7 +1481,7 @@ var
parser: TsNumFormatParser;
nf: TsNumberFormat;
begin
parser := TsNumFormatParser.Create(Workbook, AFormatString, cdToFPSpreadsheet);
parser := TsNumFormatParser.Create(Workbook, AFormatString, nfCustom, cdToFPSpreadsheet);
try
// Format string ok?
if parser.Status <> psOK then
@ -1533,18 +1557,17 @@ end;
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
as a date (either built-in or a custom format).
Note: custom formats are currently not supported by the writer.
}
procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
var
ACell: PCell;
fmt: String;
parser: TsNumFormatParser;
//fmt: String;
//parser: TsNumFormatParser;
begin
if AFormat = nfFmtDateTime then begin
if (AFormat in [nfFmtDateTime, nfTimeInterval]) then
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
(*
parser := TsNumFormatParser.Create(Workbook, AFormatStr, cdToFPSpreadsheet);
try
// Check that the format string can be reckognized.
@ -1558,6 +1581,7 @@ begin
parser.Free;
end;
end;
*)
ACell := GetCell(ARow, ACol);
ACell^.ContentType := cctDateTime;
@ -1579,7 +1603,8 @@ end;
procedure TsWorksheet.WriteDecimals(ACell: PCell; ADecimals: Byte);
begin
if (ACell <> nil) and (ACell^.ContentType = cctNumber) then 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);
@ -2629,27 +2654,43 @@ end;
{ Adds a new number format data to the list and returns the list index of the
new (or present) item. }
function TsCustomNumFormatList.AddFormat(AFormatIndex: Integer; ANumFormat: TsNumberFormat;
AFormatString: String = ''; ADecimals: byte = 0; ACurrencySymbol: String = ''): integer;
function TsCustomNumFormatList.AddFormat(AFormatIndex: Integer;
AFormatString: String; ANumFormat: TsNumberFormat; ADecimals: byte = 0;
ACurrencySymbol: String = ''): integer;
var
item: TsNumFormatData;
begin
item := TsNumFormatData.Create;
item.Index := AFormatIndex;
item.NumFormat := ANumFormat;
if IsDateTimeFormat(ANumFormat) then
AFormatString := BuildDateTimeFormatString(ANumFormat, Workbook.FormatSettings,
AFormatString)
else
if item.NumFormat <> nfCustom then
AFormatString := BuildNumberFormatString(ANumFormat, Workbook.FormatSettings,
ADecimals, ACurrencySymbol);
if AFormatString = '' then begin
if IsDateTimeFormat(ANumFormat) then
AFormatString := BuildDateTimeFormatString(ANumFormat, Workbook.FormatSettings,
AFormatString)
else
if item.NumFormat <> nfCustom then
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(AFormatString: String;
ANumFormat: TsNumberFormat; ADecimals: Byte = 0;
ACurrencySymbol: String = ''): Integer;
begin
if AFormatString = '' then begin
Result := 0;
exit;
end;
Result := AddFormat(FNextFormatIndex, AFormatString, ANumFormat, ADecimals,
ACurrencySymbol);
inc(FNextFormatIndex);
end;
function TsCustomNumFormatList.AddFormat(AFormatCell: PCell): Integer;
var
item: TsNumFormatData;
@ -2661,8 +2702,8 @@ begin
raise Exception.Create('TsCustomNumFormatList: Error in program logics: You must provide built-in formats first.');
Result := AddFormat(FNextFormatIndex,
AFormatCell^.NumberFormat,
AFormatCell^.NumberFormatStr,
AFormatCell^.NumberFormat,
AFormatCell^.Decimals,
AFormatCell^.CurrencySymbol
);
@ -2671,25 +2712,24 @@ begin
end;
{ Adds the builtin format items to the list. The formats must be specified in
a way which can be understood by fpc.
If fpc and file speak different languages "translation" must be made in
"Analyze" for reading and "FormatStringForWriting" for writing.
Must be called before user items are added.
a way that is compatible with the destination file format. Conversion of the
formatstrings can be done by calling "ConvertAfterReadung" bzw. "ConvertBeforeWriting".
"AddBuiltInFormats" must be called before user items are added.
Must specify FFirstFormatIndexInFile (BIFF5-8, e.g. doesn't save formats <164)
and must initialize the index of the first user format (FNextFormatIndex)
which is automatically incremented when adding user formats. }
procedure TsCustomNumFormatList.AddBuiltinFormats;
begin
// must be overridden
// must be overridden - see xlscommon as an example.
end;
{ Takes the format string (AFormatString) as it is read from the file and
extracts the number format type (ANumFormat) and the number of decimals
(ADecimals) out of it for use by fpc.
If the format string cannot be directly handled by fpc it has to be transformed
to make it compatible. Can be done in overridden versions which know more
about the structure of the string in the actual file format. }
procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer;
extracts the number format type and the number of decimals out of it for use by
fpc. The method also converts the format string to a form that can be used
by fpc's FormatDateTime and FormatFloat. This conversion should be done in an
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
@ -2697,6 +2737,7 @@ var
fmt: String;
lFormatData: TsNumFormatData;
i: Integer;
nf: TsNumberFormat;
begin
i := Find(AFormatIndex);
if i > 0 then begin
@ -2704,16 +2745,42 @@ begin
fmt := lFormatData.FormatString;
end else
fmt := AFormatString;
nf := nfGeneral; // not used here.
parser := TsNumFormatParser.Create(Workbook, fmt, cdToFPSpreadsheet);
// Analyzes the format string and tries to convert it to fpSpreadsheet format.
parser := TsNumFormatParser.Create(Workbook, fmt, nf, cdToFPSpreadsheet);
try
if parser.Status = psOK then begin
ANumFormat := parser.Builtin_NumFormat;
AFormatString := parser.FormatString;
AFormatString := parser.FormatString; // This is the converted string.
{
if not (parser.Builtin_NumFormat in [nfCustom, nfFmtDateTime]) then begin
ADecimals := parser.ParsedSections[0].Decimals;
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
end;
}
end;
finally
parser.Free;
end;
end;
{ Is called before collection 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. }
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;
@ -2842,11 +2909,11 @@ begin
if Find(AFormatIndex) > -1 then
exit;
// Analyze the format string and extract information for internal formatting
Analyze(AFormatIndex, AFormatString, nf, decs, currsym);
// Analyze & convert the format string, extract infos for internal formatting
ConvertAfterReading(AFormatIndex, AFormatString, nf, decs, currsym);
// Add the new item
AddFormat(AFormatIndex, nf, AFormatString, decs, currSym);
AddFormat(AFormatIndex, AFormatString, nf, decs, currSym);
end;
{ Clears the list and frees memory occupied by the format items. }
@ -2967,6 +3034,20 @@ begin
Result := -1;
end;
{ Finds the item with the given format string and returns its index in the
format list. }
function TsCustomNumFormatList.Find(AFormatString: String): integer;
var
item: TsNumFormatData;
begin
for Result := 0 to Count-1 do begin
item := Items[Result];
if item.FormatString = AFormatString then
exit;
end;
Result := -1;
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. }
@ -3155,7 +3236,7 @@ begin
if (FFormattingStyles[i].Decimals <> AFormat^.Decimals) then Continue;
if (FFormattingStyles[i].CurrencySymbol <> AFormat^.CurrencySymbol) then Continue;
end;
nfShortDate, nfLongDate, nfShortDateTime, nfShortTime, nfLongTime,
nfShortDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime,
nfShortTimeAM, nfLongTimeAM, nfFmtDateTime, nfTimeInterval, nfCustom:
if (FFormattingstyles[i].NumberFormatStr <> AFormat^.NumberFormatStr) then Continue;
end;
@ -3173,6 +3254,10 @@ end;
format of the writer, here is the place to apply replacements.
Must be overridden by descendants. See BIFF2 }
procedure TsCustomSpreadWriter.FixFormat(ACell: PCell);
begin
// to be overridden
end;
(*
var
isLong, isAMPM, isInterval: Boolean;
decs: Byte;
@ -3182,7 +3267,7 @@ begin
// if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then
ACell^.Decimals := decs;
end;
end;
end; *)
{ Each descendent should define its own default formats, if any.
Always add the normal, unformatted style first to speed things up. }
@ -3210,6 +3295,26 @@ begin
Len := Length(FFormattingStyles);
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;
Inc(NextXFIndex);
end;
@ -3220,12 +3325,24 @@ var
begin
SetLength(FFormattingStyles, 0);
// Add default styles which are required to be there by the destination file
AddDefaultFormats();
// Iterate through all cells and collect the individual styles
for i := 0 to Workbook.GetWorksheetCount - 1 do
begin
IterateThroughCells(nil, Workbook.GetWorksheetByIndex(i).Cells, ListAllFormattingStylesCallback);
end;
// 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
(FFormattingStyles[i].NumberFormat <> nfCustom) // don't touch custom formatstrings!
then
FNumFormatList.ConvertBeforeWriting(
FFormattingStyles[i].NumberFormatStr,
FFormattingStyles[i].NumberFormat,
FFormattingStyles[i].Decimals,
FFormattingStyles[i].CurrencySymbol
);
end;
{@@
@ -3233,10 +3350,34 @@ end;
it does not yet exist in the list.
}
procedure TsCustomSpreadWriter.ListAllNumFormatsCallback(ACell: PCell; AStream: TStream);
var
fmt: string;
nf: TsNumberFormat;
decs: Byte;
cs: String;
begin
FixFormat(ACell);
if FNumFormatList.Find(ACell) = -1 then
FNumFormatList.AddFormat(ACell);
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".
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);
end;
{@@
@ -3265,7 +3406,7 @@ begin
ResPos := -1;
SetLength(Result, 0);
// The formula needs to start with a =
// The formula needs to start with a "=" character.
if AFormula.FormulaStr[1] <> '=' then raise Exception.Create('Formula doesn''t start with =');
StrPos := 2;

View File

@ -84,6 +84,7 @@ function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; AFormatString: String = ''): String;
function StripAMPM(const ATimeFormatString: String): String;
function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte;
function AddIntervalBrackets(AFormatString: String): String;
function SciFloat(AValue: Double; ADecimals: Byte): String;
//function TimeIntervalToString(AValue: TDateTime; AFormatStr: String): String;
@ -517,7 +518,7 @@ end;
function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean;
begin
Result := AFormat in [nfFmtDateTime, nfShortDateTime, nfShortDate, nfLongDate,
nfShortTime. nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval];
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval];
end;
(*
{ This simple parsing procedure of the Excel format string checks for a fixed
@ -685,7 +686,7 @@ begin
if ph > 0 then IsSci := true;
end;
end;
*)
{ IsDateFormat checks if the format string s corresponds to a date format }
function IsDateFormat(s: String; out IsLong: Boolean): Boolean;
begin
@ -767,6 +768,7 @@ begin
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
@ -776,6 +778,7 @@ function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat;
const AFormatSettings: TFormatSettings; AFormatString: String = '') : string;
var
fmt: String;
am, pm: String;
begin
case ANumberFormat of
nfFmtDateTime:
@ -789,31 +792,38 @@ begin
end;
nfShortDateTime:
Result := AFormatSettings.ShortDateFormat + ' ' + FormatSettings.ShortTimeFormat;
// In the DefaultFormatSettings this is: d/m/y hh:nn
nfShortDate:
Result := AFormatSettings.ShortDateFormat;
Result := AFormatSettings.ShortDateFormat; // --> d/m/y
nfLongDate:
Result := AFormatSettings.LongDateFormat;
Result := AFormatSettings.LongDateFormat; // --> dd mm yyyy
nfShortTime:
Result := StripAMPM(AFormatSettings.ShortTimeFormat);
Result := StripAMPM(AFormatSettings.ShortTimeFormat); // --> hh:nn
nfLongTime:
Result := StripAMPM(AFormatSettings.LongTimeFormat);
Result := StripAMPM(AFormatSettings.LongTimeFormat); // --> hh:nn:ss
nfShortTimeAM:
begin
begin // --> hh:nn AM/PM
Result := AFormatSettings.ShortTimeFormat;
if pos('a', lowercase(AFormatSettings.ShortTimeFormat)) = 0 then
Result := Format('%s %s/%s', [Result, AFormatSettings.TimeAMString, AFormatSettings.TimePMString]);
if (pos('a', lowercase(AFormatSettings.ShortTimeFormat)) = 0) then begin
am := IfThen(AFormatSettings.TimeAMString = '', 'AM', AFormatSettings.TimeAMString);
pm := IfThen(AFormatSettings.TimePMString = '', 'PM', AFormatSettings.TimePMString);
Result := Format('%s %s/%s', [Result, am, pm]);
end;
end;
nfLongTimeAM:
nfLongTimeAM: // --> hh:nn:ss AM/PM
begin
Result := AFormatSettings.LongTimeFormat;
if pos('a', lowercase(AFormatSettings.LongTimeFormat)) = 0 then
Result := Format('%s %s/%s', [Result, AFormatSettings.TimeAMString, AFormatSettings.TimePMString]);
if pos('a', lowercase(AFormatSettings.LongTimeFormat)) = 0 then begin
am := IfThen(AFormatSettings.TimeAMString = '', 'AM', AFormatSettings.TimeAMString);
pm := IfThen(AFormatSettings.TimePMString = '', 'PM', AFormatSettings.TimePMString);
Result := Format('%s %s/%s', [Result, am, pm]);
end;
end;
nfTimeInterval:
nfTimeInterval: // --> [h]:nn:ss
if AFormatString = '' then
Result := '[h]:mm:ss'
else
Result := AFormatString;
Result := AddIntervalBrackets(AFormatString);
end;
end;
@ -886,13 +896,16 @@ begin
else Result := Result + ';#,##0' + decs + '-';
end;
end;
if ANumberFormat in [nfCurrency, nfCurrencyRed] then begin
if ANumberFormat in [nfCurrency, nfCurrencyRed] then
Result := Result +';' + Format(POS_FMT[cf], ['0' + decs, ACurrencySymbol])
{
Result := Result + ';0' + decs;
if cf in [2,3] then
Result := Format('%s "%s"', [Result, ACurrencySymbol])
else
Result := Format('%s"%s"', [Result, ACurrencySymbol]);
end else
}
else
Result := Result + ';-';
end;
end;
@ -928,6 +941,26 @@ begin
Result := 0;
end;
{ The given format string is assumed to be for time intervals, i.e. its first
time symbol must be enclosed by square brackets. Checks if this is true, and
adds the brackes if not. }
function AddIntervalBrackets(AFormatString: String): String;
var
p: Integer;
s1, s2: String;
begin
if AFormatString[1] = '[' then
Result := AFormatString
else begin
p := pos(':', AFormatString);
if p <> 0 then begin
s1 := copy(AFormatString, 1, p-1);
s2 := copy(AFormatString, p, Length(AFormatString));
Result := Format('[%s]%s', [s1, s2]);
end else
Result := Format('[%s]', [AFormatString]);
end;
end;
{ Formats the number AValue in "scientific" format with the given number of
decimals. "Scientific" is the same as "exponential", but with exponents rounded

View File

@ -44,7 +44,7 @@ type
procedure AddBuiltinFormats; override;
public
constructor Create(AWorkbook: TsWorkbook);
function FormatStringForWriting(AIndex: Integer): String; override;
// function FormatStringForWriting(AIndex: Integer): String; override;
end;
{ TsSpreadBIFF2Reader }
@ -173,33 +173,34 @@ end;
procedure TsBIFF2NumFormatList.AddBuiltinFormats;
begin
AddFormat( 0, nfGeneral);
AddFormat( 1, nfFixed, '0', 0);
AddFormat( 2, nfFixed, '0.00', 2);
AddFormat( 3, nfFixedTh, '#,##0', 0);
AddFormat( 4, nfFixedTh, '#,##0.00', 2);
AddFormat( 5, nfFixedTh, '"$"#,##0_);("$"#,##0)', 0);
AddFormat( 6, nfFixedTh, '"$"#,##0_);[Red]("$"#,##0)', 2);
AddFormat( 7, nfFixedTh, '"$"#,##0.00_);("$"#,##0.00)', 0);
AddFormat( 8, nfFixedTh, '"$"#,##0.00_);[Red]("$"#,##0.00)', 2);
AddFormat( 9, nfPercentage, '0%', 0);
AddFormat(10, nfPercentage, '0.00%', 2);
AddFormat(11, nfExp, '0.00E+00', 2);
AddFormat(12, nfShortDate);
AddFormat(13, nfLongDate);
AddFormat(14, nfFmtDateTime, 'd-mmm');
AddFormat(15, nfFmtDateTime, 'mmm-yy');
AddFormat(16, nfShortTimeAM);
AddFormat(17, nfLongTimeAM);
AddFormat(18, nfShortTime);
AddFormat(19, nfLongTime);
AddFormat(20, nfShortDateTime);
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, '"$"#,##0_);("$"#,##0)', nfCurrency, 0);
AddFormat( 6, '"$"#,##0_);[Red]("$"#,##0)', nfCurrencyRed, 2);
AddFormat( 7, '"$"#,##0.00_);("$"#,##0.00)', nfCurrency, 0);
AddFormat( 8, '"$"#,##0.00_);[Red]("$"#,##0.00)', nfCurrency, 2);
AddFormat( 9, '0%', nfPercentage, 0);
AddFormat(10, '0.00%', nfPercentage, 2);
AddFormat(11, '0.00E+00', nfExp, 2);
AddFormat(12, 'M/D/YY', nfShortDate);
AddFormat(13, 'D-MMM-YY', nfLongDate);
AddFormat(14, 'D-MMM', nfFmtDateTime);
AddFormat(15, 'MMM-YY', nfFmtDateTime);
AddFormat(16, 'h:mm AM/PM', nfShortTimeAM);
AddFormat(17, 'h:mm:ss AM/PM', nfLongTimeAM);
AddFormat(18, 'h:mm', nfShortTime);
AddFormat(19, 'h:mm:ss', nfLongTime);
AddFormat(20, 'M/D/YY h:mm', nfShortDateTime);
FFirstFormatIndexInFile := 0; // BIFF2 stores built-in formats to file.
FNextFormatIndex := 21; // not needed - there are not user-defined formats
end;
{ Creates formatting strings that are written into the file. }
(*
function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String;
var
ds, ts, cs: string;
@ -231,7 +232,7 @@ begin
20: Result := 'm/d/yy h:mm';
end;
end;
*)
{ TsSpreadBIFF2Reader }

View File

@ -348,11 +348,14 @@ type
TsBIFFNumFormatList = class(TsCustomNumFormatList)
protected
procedure AddBuiltinFormats; override;
procedure Analyze(AFormatIndex: Integer; var AFormatString: String;
{
procedure ConvertAfterReading(AFormatIndex: Integer; var AFormatString: String;
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
var ACurrencySymbol: String); override;
procedure ConvertBeforeWriting(var AFormatString: String); override;
}
public
function FormatStringForWriting(AIndex: Integer): String; override;
// function FormatStringForWriting(AIndex: Integer): String; override;
end;
{ TsSpreadBIFFReader }
@ -535,50 +538,50 @@ end;
{ TsBIFFNumFormatList }
{ These are the built-in number formats as used by fpc. Before writing to file
some code will be modified to become compatible with Excel
(--> FormatStringForWriting) }
{ 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. }
procedure TsBIFFNumFormatList.AddBuiltinFormats;
var
cs: String;
begin
cs := DefaultFormatSettings.CurrencyString;
cs := Workbook.FormatSettings.CurrencyString;
AddFormat( 0, nfGeneral);
AddFormat( 1, nfFixed, '0', 0);
AddFormat( 2, nfFixed, '0.00', 2);
AddFormat( 3, nfFixedTh, '#,##0', 0);
AddFormat( 4, nfFixedTh, '#,##0.00', 2);
AddFormat( 5, nfCurrency, '', 0);
AddFormat( 6, nfCurrencyRed, '', 0); // negative numbers in red
AddFormat( 7, nfCurrency, '', 2);
AddFormat( 8, nfCurrencyRed, '', 2);
AddFormat( 9, nfPercentage, '0%', 0);
AddFormat(10, nfPercentage, '0.00%', 2);
AddFormat(11, nfExp, '0.00E+00', 2);
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);
// fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported
AddFormat(14, nfShortDate);
AddFormat(15, nfLongDate);
AddFormat(16, nfFmtDateTime, 'd/mmm');
AddFormat(17, nfFmtDateTime, 'mmm/yy');
AddFormat(18, nfShortTimeAM);
AddFormat(19, nfLongTimeAM);
AddFormat(20, nfShortTime);
AddFormat(21, nfLongTime);
AddFormat(22, nfShortDateTime);
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);
// 23..36 not supported
AddFormat(37, nfCurrency, '', 0);
AddFormat(38, nfCurrencyRed, '', 0);
AddFormat(39, nfCurrency, '', 2);
AddFormat(40, nfCurrencyRed, '', 2);
AddFormat(41, nfCurrencyDash, '', 0);
AddFormat(42, nfCurrencyDashRed, '', 0);
AddFormat(43, nfCurrencyDash, '', 2);
AddFormat(44, nfCurrencyDashRed, '', 2);
AddFormat(45, nfFmtDateTime, 'nn:ss');
AddFormat(46, nfTimeInterval, '[h]:nn:ss');
AddFormat(47, nfFmtDateTime, 'nn:ss.z'); // z will be replaced by 0 later
AddFormat(48, nfSci, '##0.0E+00', 1);
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+'"* "-"_);_(@_)', nfCurrencyDash, 0);
AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfCurrencyDash, 0);
AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfCurrencyDash, 2);
AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfCurrencyDash, 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);
// 49 ("Text") not supported
// All indexes from 0 to 163 are reserved for built-in formats.
@ -586,7 +589,7 @@ begin
FFirstFormatIndexInFile := 164;
FNextFormatIndex := 164;
end;
(*
{ Considers some Excel specialities for format detection.
The output values will be passed to fpc. }
procedure TsBIFFNumFormatList.Analyze(AFormatIndex: Integer;
@ -600,7 +603,7 @@ var
}
begin
(*
{
AFormatString := 'hh:mm:ss.0 AM/PM'; //"€" #,##.0;[red]"$" -#,##.000;-';
parser := TsNumFormatParser.Create(Workbook, AFormatString);
@ -620,7 +623,7 @@ begin
finally
parser.Free;
end;
*)
}
fmt := Lowercase(AFormatString);
{ Check the built-in formats first:
@ -664,7 +667,8 @@ begin
inherited Analyze(AFormatIndex, AFormatString, ANumFormat, ADecimals, ACurrencySymbol);
end;
*)
(*
{ Creates formatting strings that are written into the file. }
function TsBIFFNumFormatList.FormatStringForWriting(AIndex: Integer): String;
var
@ -712,7 +716,7 @@ begin
end;
end;
end;
*)
{ TsSpreadBIFFReader }
@ -2039,6 +2043,7 @@ procedure TsSpreadBIFFWriter.WriteXFIndex(AStream: TStream; ACell: PCell);
var
lIndex: Integer;
lXFIndex: Word;
lCell: TCell;
begin
// First try the fast methods for default formats
if ACell^.UsedFormattingFields = [] then begin
@ -2047,7 +2052,22 @@ begin
end;
// If not, then we need to search in the list of dynamic formats
lIndex := FindFormattingInList(ACell);
// 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^;
// CopyCellFormat(ACell, @lCell);
with lCell do begin
if NumberFormat <> nfCustom then begin
if IsDateTimeFormat(NumberFormat) then
NumberFormatStr := BuildDateTimeFormatString(NumberFormat,
Workbook.FormatSettings, NumberFormatStr)
else
NumberFormatStr := BuildNumberFormatString(NumberFormat,
Workbook.FormatSettings, Decimals, CurrencySymbol);
NumFormatList.ConvertBeforeWriting(NumberFormatStr, NumberFormat, Decimals, CurrencyString);
end;
end;
lIndex := FindFormattingInList(@lCell);
// Carefully check the index
if (lIndex < 0) or (lIndex > Length(FFormattingStyles)) then