You've already forked lazarus-ccr
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:
@ -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;
|
||||
|
@ -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', '[':
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user