You've already forked lazarus-ccr
fpspreadsheet: Bug fixes for biff2 reading/writing for number formats
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3075 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@@ -43,7 +43,7 @@ begin
|
|||||||
MyWorksheet.LeftPaneWidth := 1;
|
MyWorksheet.LeftPaneWidth := 1;
|
||||||
MyWorksheet.TopPaneHeight := 3;
|
MyWorksheet.TopPaneHeight := 3;
|
||||||
}
|
}
|
||||||
|
(*
|
||||||
// Write some number cells
|
// Write some number cells
|
||||||
MyWorksheet.WriteNumber(0, 0, 1.0);
|
MyWorksheet.WriteNumber(0, 0, 1.0);
|
||||||
MyWorksheet.WriteUsedFormatting(0, 0, [uffBold, uffNumberFormat]);
|
MyWorksheet.WriteUsedFormatting(0, 0, [uffBold, uffNumberFormat]);
|
||||||
@@ -82,7 +82,7 @@ begin
|
|||||||
MyWorksheet.WriteUTF8Text(1, 1, 'Second');
|
MyWorksheet.WriteUTF8Text(1, 1, 'Second');
|
||||||
MyWorksheet.WriteUTF8Text(1, 2, 'Third');
|
MyWorksheet.WriteUTF8Text(1, 2, 'Third');
|
||||||
MyWorksheet.WriteUTF8Text(1, 3, 'Fourth');
|
MyWorksheet.WriteUTF8Text(1, 3, 'Fourth');
|
||||||
|
*)
|
||||||
// Write current date/time
|
// Write current date/time
|
||||||
MyWorksheet.WriteDateTime(2, 0, now);
|
MyWorksheet.WriteDateTime(2, 0, now);
|
||||||
|
|
||||||
@@ -267,7 +267,6 @@ begin
|
|||||||
MyWorksheet.WriteFontColor(r, 3, scGray);
|
MyWorksheet.WriteFontColor(r, 3, scGray);
|
||||||
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3);
|
MyWorksheet.WriteNumber(r, 4, -1.0/number, nfExp, 3);
|
||||||
MyWorksheet.WriteFontColor(r, 4, scGray);
|
MyWorksheet.WriteFontColor(r, 4, scGray);
|
||||||
|
|
||||||
inc(r,2);
|
inc(r,2);
|
||||||
MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs');
|
MyWorksheet.WriteUTF8Text(r, 0, 'nfCurrency, 0 decs');
|
||||||
MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, '$');
|
MyWorksheet.WriteNumber(r, 1, number, nfCurrency, 0, '$');
|
||||||
@@ -288,7 +287,6 @@ begin
|
|||||||
MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDashRed, 0, 'USD');
|
MyWorksheet.WriteNumber(r, 1, number, nfCurrencyDashRed, 0, 'USD');
|
||||||
MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDashRed, 0, 'USD');
|
MyWorksheet.WriteNumber(r, 2, -number, nfCurrencyDashRed, 0, 'USD');
|
||||||
MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDashRed, 0, 'USD');
|
MyWorksheet.WriteNumber(r, 3, 0.0, nfCurrencyDashRed, 0, 'USD');
|
||||||
|
|
||||||
inc(r, 2);
|
inc(r, 2);
|
||||||
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)');
|
MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)');
|
||||||
MyWorksheet.WriteNumber(r, 1, number);
|
MyWorksheet.WriteNumber(r, 1, number);
|
||||||
|
@@ -55,7 +55,6 @@ begin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write some cells
|
// Write some cells
|
||||||
// MyWorksheet.WriteDateTime(0, 20, now, nfShortTime); //1.0);// A1
|
|
||||||
MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1
|
MyWorksheet.WriteNumber(0, 0, 1.0, nfFixed, 3);// A1
|
||||||
MyWorksheet.WriteNumber(0, 1, 2.0);// B1
|
MyWorksheet.WriteNumber(0, 1, 2.0);// B1
|
||||||
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
|
MyWorksheet.WriteNumber(0, 2, 3.0);// C1
|
||||||
@@ -133,7 +132,7 @@ begin
|
|||||||
MyWorksheet.WriteFont(8, 3, 'Courier New', 12, [fssUnderline], scBlue);
|
MyWorksheet.WriteFont(8, 3, 'Courier New', 12, [fssUnderline], scBlue);
|
||||||
MyWorksheet.WriteBackgroundColor(8, 3, scYellow);
|
MyWorksheet.WriteBackgroundColor(8, 3, scYellow);
|
||||||
|
|
||||||
(*
|
{
|
||||||
// Uncomment this to test large XLS files
|
// Uncomment this to test large XLS files
|
||||||
for i := 50 to 1000 do
|
for i := 50 to 1000 do
|
||||||
begin
|
begin
|
||||||
@@ -142,7 +141,7 @@ begin
|
|||||||
// MyWorksheet.WriteUTF8Text(i, 2, ParamStr(0));
|
// MyWorksheet.WriteUTF8Text(i, 2, ParamStr(0));
|
||||||
MyWorksheet.WriteUTF8Text(i, 3, ParamStr(0));
|
MyWorksheet.WriteUTF8Text(i, 3, ParamStr(0));
|
||||||
end;
|
end;
|
||||||
*)
|
}
|
||||||
|
|
||||||
// Write the formula E1 = A1 + B1
|
// Write the formula E1 = A1 + B1
|
||||||
SetLength(MyRPNFormula, 3);
|
SetLength(MyRPNFormula, 3);
|
||||||
|
@@ -550,11 +550,17 @@ begin
|
|||||||
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
|
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
|
||||||
end;
|
end;
|
||||||
'm':
|
'm':
|
||||||
|
if FConversionDirection = cdToFPSpreadsheet then
|
||||||
|
ScanDateTimeParts(token, 'n', s)
|
||||||
|
else
|
||||||
|
ScanDateTimeParts(token, 'm', s);
|
||||||
|
{
|
||||||
if FConversionDirection = cdToFPSpreadsheet then begin
|
if FConversionDirection = cdToFPSpreadsheet then begin
|
||||||
if FIsTime then ScanDateTimeParts(token, 'n', s) else ScanDateTimeParts(token, 'm', s)
|
if FIsTime then ScanDateTimeParts(token, 'n', s) else ScanDateTimeParts(token, 'm', s)
|
||||||
end else begin
|
end else begin
|
||||||
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
|
if FIsTime then ScanDateTimeParts(token, 'm', s) else ScanDateTimeParts(token, 'M', s);
|
||||||
end;
|
end;
|
||||||
|
}
|
||||||
'N', 'n':
|
'N', 'n':
|
||||||
if FConversionDirection = cdToFPSpreadsheet then begin
|
if FConversionDirection = cdToFPSpreadsheet then begin
|
||||||
// "n" is not used by file format --> stop scanning date/time
|
// "n" is not used by file format --> stop scanning date/time
|
||||||
|
@@ -581,11 +581,11 @@ type
|
|||||||
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
|
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
|
||||||
var ACurrencySymbol: String); virtual;
|
var ACurrencySymbol: String); virtual;
|
||||||
procedure Delete(AIndex: Integer);
|
procedure Delete(AIndex: Integer);
|
||||||
function Find(AFormatCell: PCell): integer; overload;
|
|
||||||
function Find(ANumFormat: TsNumberFormat; AFormatString: String;
|
function Find(ANumFormat: TsNumberFormat; AFormatString: String;
|
||||||
ADecimals: Byte; ACurrencySymbol: String): Integer; overload;
|
ADecimals: Byte; ACurrencySymbol: String): Integer; overload;
|
||||||
function Find(AFormatIndex: Integer): Integer; overload;
|
function Find(AFormatIndex: Integer): Integer; overload;
|
||||||
function Find(AFormatString: String): Integer; overload;
|
function Find(AFormatString: String): Integer; overload;
|
||||||
|
function FindFormatOf(AFormatCell: PCell): integer; virtual;
|
||||||
function FormatStringForWriting(AIndex: Integer): String; virtual;
|
function FormatStringForWriting(AIndex: Integer): String; virtual;
|
||||||
procedure Sort;
|
procedure Sort;
|
||||||
|
|
||||||
@@ -642,7 +642,7 @@ type
|
|||||||
function FindFormattingInList(AFormat: PCell): Integer;
|
function FindFormattingInList(AFormat: PCell): Integer;
|
||||||
procedure FixFormat(ACell: PCell); virtual;
|
procedure FixFormat(ACell: PCell); virtual;
|
||||||
procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream);
|
procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream);
|
||||||
procedure ListAllFormattingStyles;
|
procedure ListAllFormattingStyles; virtual;
|
||||||
procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream);
|
procedure ListAllNumFormatsCallback(ACell: PCell; AStream: TStream);
|
||||||
procedure ListAllNumFormats; virtual;
|
procedure ListAllNumFormats; virtual;
|
||||||
{ Helpers for writing }
|
{ Helpers for writing }
|
||||||
@@ -999,11 +999,6 @@ procedure TsWorksheet.CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal;
|
|||||||
AFromWorksheet: TsWorksheet);
|
AFromWorksheet: TsWorksheet);
|
||||||
var
|
var
|
||||||
lSrcCell, lDestCell: PCell;
|
lSrcCell, lDestCell: PCell;
|
||||||
{
|
|
||||||
lCurStr: String;
|
|
||||||
lCurUsedFormatting: TsUsedFormattingFields;
|
|
||||||
lCurColor: TsColor;
|
|
||||||
}
|
|
||||||
begin
|
begin
|
||||||
lSrcCell := AFromWorksheet.FindCell(AFromRow, AFromCol);
|
lSrcCell := AFromWorksheet.FindCell(AFromRow, AFromCol);
|
||||||
lDestCell := GetCell(AToRow, AToCol);
|
lDestCell := GetCell(AToRow, AToCol);
|
||||||
@@ -1564,26 +1559,9 @@ procedure TsWorksheet.WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime;
|
|||||||
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
|
AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = '');
|
||||||
var
|
var
|
||||||
ACell: PCell;
|
ACell: PCell;
|
||||||
//fmt: String;
|
|
||||||
//parser: TsNumFormatParser;
|
|
||||||
begin
|
begin
|
||||||
if (AFormat in [nfFmtDateTime, nfTimeInterval]) then
|
if (AFormat in [nfFmtDateTime, nfTimeInterval]) then
|
||||||
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
|
AFormatStr := BuildDateTimeFormatString(AFormat, Workbook.FormatSettings, AFormatStr);
|
||||||
(*
|
|
||||||
parser := TsNumFormatParser.Create(Workbook, AFormatStr, cdToFPSpreadsheet);
|
|
||||||
try
|
|
||||||
// Check that the format string can be reckognized.
|
|
||||||
if parser.Status <> psOK then
|
|
||||||
raise Exception.Create(lpNoValidNumberFormatString);
|
|
||||||
// Check that the format string belongs to date/time values
|
|
||||||
if not (IsDateTimeFormat(parser.Builtin_NumFormat) or (parser.Builtin_NumFormat = nfFmtDateTime))
|
|
||||||
then raise Exception.Create(lpNoValidDateTimeFormatString);
|
|
||||||
AFormatStr := parser.FormatString;
|
|
||||||
finally
|
|
||||||
parser.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
|
|
||||||
ACell := GetCell(ARow, ACol);
|
ACell := GetCell(ARow, ACol);
|
||||||
ACell^.ContentType := cctDateTime;
|
ACell^.ContentType := cctDateTime;
|
||||||
@@ -2268,9 +2246,7 @@ end;
|
|||||||
It is added to the end of the list of worksheets
|
It is added to the end of the list of worksheets
|
||||||
|
|
||||||
@param AName The name of the new worksheet
|
@param AName The name of the new worksheet
|
||||||
|
|
||||||
@return The instace of the newly created worksheet
|
@return The instace of the newly created worksheet
|
||||||
|
|
||||||
@see TsWorkbook
|
@see TsWorkbook
|
||||||
}
|
}
|
||||||
function TsWorkbook.AddWorksheet(AName: string): TsWorksheet;
|
function TsWorkbook.AddWorksheet(AName: string): TsWorksheet;
|
||||||
@@ -2790,114 +2766,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(*
|
|
||||||
procedure TsCustomNumFormatList.Analyze(AFormatIndex: Integer;
|
|
||||||
var AFormatString: String; var ANumFormat: TsNumberFormat;
|
|
||||||
var ADecimals: Byte; var ACurrencySymbol: String);
|
|
||||||
const
|
|
||||||
SHORT_LONG_DATE: array[boolean] of TsNumberFormat = (
|
|
||||||
nfShortDate, nfLongDate
|
|
||||||
);
|
|
||||||
AMPM_SHORT_LONG_TIME: array[boolean, boolean] of TsNumberFormat = (
|
|
||||||
(nfShortTime, nfLongTime),
|
|
||||||
(nfShortTimeAM, nfLongTimeAM)
|
|
||||||
);
|
|
||||||
EXP_SCI: array[boolean] of TsNumberFormat = (
|
|
||||||
nfExp, nfSci
|
|
||||||
);
|
|
||||||
CURR_FMT: array[boolean, boolean] of TsNumberFormat = (
|
|
||||||
(nfCurrency, nfCurrencyDash),
|
|
||||||
(nfCurrencyRed, nfCurrencyDashRed)
|
|
||||||
);
|
|
||||||
var
|
|
||||||
decs: Word;
|
|
||||||
isAMPM: Boolean;
|
|
||||||
isLongTime: Boolean;
|
|
||||||
isLongDate: Boolean;
|
|
||||||
isInterval: Boolean;
|
|
||||||
isSci: Boolean;
|
|
||||||
isTime, isDate: Boolean;
|
|
||||||
isCurrRed, isCurrDash: Boolean;
|
|
||||||
lFormatData: TsNumFormatData;
|
|
||||||
i: Integer;
|
|
||||||
fmt: String;
|
|
||||||
begin
|
|
||||||
{ Check the built-in formats first }
|
|
||||||
i := Find(AFormatIndex);
|
|
||||||
if i > 0 then begin
|
|
||||||
lFormatData := Items[i];
|
|
||||||
case lFormatData.NumFormat of
|
|
||||||
nfFixed, nfFixedTh, nfPercentage, nfExp, nfSci:
|
|
||||||
begin
|
|
||||||
ANumFormat := lFormatData.NumFormat;
|
|
||||||
AFormatString := lFormatData.FormatString;
|
|
||||||
ADecimals := lFormatData.Decimals;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
nfShortDateTime, nfShortDate, nfLongDate,
|
|
||||||
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval:
|
|
||||||
begin
|
|
||||||
ANumFormat := lFormatData.NumFormat;
|
|
||||||
AFormatString := lFormatData.FormatString;
|
|
||||||
ADecimals := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
nfFmtDateTime:
|
|
||||||
begin
|
|
||||||
ANumFormat := lFormatData.NumFormat;
|
|
||||||
AFormatString := lFormatData.FormatString;
|
|
||||||
IsTimeFormat(AFormatString, isLongTime, isAMPM, isInterval, ADecimals);
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed:
|
|
||||||
begin
|
|
||||||
ANumFormat := lFormatData.NumFormat;
|
|
||||||
AFormatString := lFormatData.FormatString;
|
|
||||||
ADecimals := lFormatData.Decimals;
|
|
||||||
ACurrencySymbol := lFormatData.CurrencySymbol;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ Then check the non-standard formats. There is a chance that they may not be
|
|
||||||
reckognized correctly... }
|
|
||||||
ANumFormat := nfGeneral;
|
|
||||||
if IsPercentNumberFormat(AFormatString, ADecimals) then
|
|
||||||
ANumFormat := nfPercentage
|
|
||||||
else
|
|
||||||
if IsExpNumberFormat(AFormatstring, ADecimals, isSci) then
|
|
||||||
ANumFormat := EXP_SCI[isSci]
|
|
||||||
else
|
|
||||||
if IsThousandSepNumberFormat(AFormatString, ADecimals) then
|
|
||||||
ANumFormat := nfFixedTh
|
|
||||||
else
|
|
||||||
if IsFixedNumberFormat(AFormatString, ADecimals) then
|
|
||||||
ANumFormat := nfFixed
|
|
||||||
else
|
|
||||||
if IsCurrencyFormat(AFormatString, ADecimals, ACurrencySymbol, isCurrRed, isCurrDash) then
|
|
||||||
ANumFormat := CURR_FMT[isCurrRed, isCurrDash]
|
|
||||||
else begin
|
|
||||||
isTime := IsTimeFormat(AFormatString, isLongTime, isAMPM, isInterval, ADecimals);
|
|
||||||
isDate := IsDateFormat(AFormatString, isLongDate);
|
|
||||||
if isInterval then
|
|
||||||
ANumFormat := nfTimeInterval
|
|
||||||
else
|
|
||||||
if isDate and isTime then
|
|
||||||
ANumFormat := nfShortDateTime
|
|
||||||
else if isDate then
|
|
||||||
ANumFormat := SHORT_LONG_DATE[isLongDate]
|
|
||||||
else if isTime then begin
|
|
||||||
if (ADecimals > 0) and (not isAMPM) then
|
|
||||||
ANumFormat := nfFmtDateTime
|
|
||||||
else
|
|
||||||
ANumFormat := AMPM_SHORT_LONG_TIME[isAMPM, isLongTime]
|
|
||||||
end
|
|
||||||
else if AFormatString <> '' then
|
|
||||||
ANumFormat := nfCustom;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
|
|
||||||
{ Called from the reader when a format item has been read from the file.
|
{ Called from the reader when a format item has been read from the file.
|
||||||
Determines the numFormat type, format string etc and stores the format in the
|
Determines the numFormat type, format string etc and stores the format in the
|
||||||
list. If necessary, the format string has to be made compatible with fpc
|
list. If necessary, the format string has to be made compatible with fpc
|
||||||
@@ -2936,17 +2804,6 @@ begin
|
|||||||
Delete(AIndex);
|
Delete(AIndex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Determines whether the format attributed to the given cell is already
|
|
||||||
contained in the list and returns its list index. }
|
|
||||||
function TsCustomNumFormatList.Find(AFormatCell: PCell): integer;
|
|
||||||
begin
|
|
||||||
if AFormatCell = nil then
|
|
||||||
Result := -1
|
|
||||||
else
|
|
||||||
Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr,
|
|
||||||
AFormatCell^.Decimals, AFormatCell^.CurrencySymbol);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ Seeks a format item with the given properties and returns its list index,
|
{ Seeks a format item with the given properties and returns its list index,
|
||||||
or -1 if not found. }
|
or -1 if not found. }
|
||||||
function TsCustomNumFormatList.Find(ANumFormat: TsNumberFormat;
|
function TsCustomNumFormatList.Find(ANumFormat: TsNumberFormat;
|
||||||
@@ -2956,17 +2813,6 @@ var
|
|||||||
fmt: String;
|
fmt: String;
|
||||||
itemfmt: String;
|
itemfmt: String;
|
||||||
begin
|
begin
|
||||||
(*
|
|
||||||
// These are pre-defined formats - no need to check format string & decimals
|
|
||||||
if ANumFormat in [ nfGeneral, nfShortDateTime, nfShortDate, nfLongDate,
|
|
||||||
nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM ]
|
|
||||||
then
|
|
||||||
for Result := 0 to Count-1 do begin
|
|
||||||
item := Items[Result];
|
|
||||||
if (item <> nil) and (item.NumFormat = ANumFormat) then
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
if (ANumFormat = nfFmtDateTime) then begin
|
if (ANumFormat = nfFmtDateTime) then begin
|
||||||
fmt := lowercase(AFormatString);
|
fmt := lowercase(AFormatString);
|
||||||
for Result := Count-1 downto 0 do begin
|
for Result := Count-1 downto 0 do begin
|
||||||
@@ -3054,6 +2900,17 @@ begin
|
|||||||
Result := -1;
|
Result := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Determines whether the format attributed to the given cell is already
|
||||||
|
contained in the list and returns its list index. }
|
||||||
|
function TsCustomNumFormatList.FindFormatOf(AFormatCell: PCell): integer;
|
||||||
|
begin
|
||||||
|
if AFormatCell = nil then
|
||||||
|
Result := -1
|
||||||
|
else
|
||||||
|
Result := Find(AFormatCell^.NumberFormat, AFormatCell^.NumberFormatStr,
|
||||||
|
AFormatCell^.Decimals, AFormatCell^.CurrencySymbol);
|
||||||
|
end;
|
||||||
|
|
||||||
{ Determines the format string to be written into the spreadsheet file.
|
{ Determines the format string to be written into the spreadsheet file.
|
||||||
Needs to be overridden if the format strings are different from the fpc
|
Needs to be overridden if the format strings are different from the fpc
|
||||||
convention. }
|
convention. }
|
||||||
@@ -3263,17 +3120,6 @@ procedure TsCustomSpreadWriter.FixFormat(ACell: PCell);
|
|||||||
begin
|
begin
|
||||||
// to be overridden
|
// to be overridden
|
||||||
end;
|
end;
|
||||||
(*
|
|
||||||
var
|
|
||||||
isLong, isAMPM, isInterval: Boolean;
|
|
||||||
decs: Byte;
|
|
||||||
begin
|
|
||||||
if ACell^.NumberFormat = nfFmtDateTime then begin
|
|
||||||
decs := CountDecs(ACell^.NumberFormatStr, ['0', 'z', 'Z']);
|
|
||||||
// if IsTimeFormat(ACell^.NumberFormatStr, isLong, isAMPM, isInterval, decs) then
|
|
||||||
ACell^.Decimals := decs;
|
|
||||||
end;
|
|
||||||
end; *)
|
|
||||||
|
|
||||||
{ Each descendent should define its own default formats, if any.
|
{ Each descendent should define its own default formats, if any.
|
||||||
Always add the normal, unformatted style first to speed things up. }
|
Always add the normal, unformatted style first to speed things up. }
|
||||||
@@ -3442,42 +3288,7 @@ begin
|
|||||||
Inc(StrPos);
|
Inc(StrPos);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
(*
|
|
||||||
function TsCustomSpreadWriter.FPSColorToHexString(AColor: TsColor; ARGBColor: TFPColor): string;
|
|
||||||
{ We use RGB bytes here, but please note that these are physically written
|
|
||||||
to XLS file as ABGR (where A is 0) }
|
|
||||||
begin
|
|
||||||
|
|
||||||
case AColor of
|
|
||||||
scBlack: Result := '000000';
|
|
||||||
scWhite: Result := 'FFFFFF';
|
|
||||||
scRed: Result := 'FF0000';
|
|
||||||
scGREEN: Result := '00FF00';
|
|
||||||
scBLUE: Result := '0000FF';
|
|
||||||
scYELLOW: Result := 'FFFF00';
|
|
||||||
scMAGENTA: Result := 'FF00FF';
|
|
||||||
scCYAN: Result := '00FFFF';
|
|
||||||
scDarkRed: Result := '800000';
|
|
||||||
scDarkGreen:Result := '008000';
|
|
||||||
scDarkBlue: Result := '000080';
|
|
||||||
scOLIVE: Result := '808000';
|
|
||||||
scPURPLE: Result := '800080';
|
|
||||||
scTEAL: Result := '008080';
|
|
||||||
scSilver: Result := 'C0C0C0';
|
|
||||||
scGrey: Result := '808080';
|
|
||||||
//
|
|
||||||
scGrey10pct:Result := 'E6E6E6';
|
|
||||||
scGrey20pct:Result := 'CCCCCC';
|
|
||||||
scOrange: Result := 'FFA500';
|
|
||||||
scDarkBrown:Result := 'A0522D';
|
|
||||||
scBrown: Result := 'CD853F';
|
|
||||||
scBeige: Result := 'F5F5DC';
|
|
||||||
scWheat: Result := 'F5DEB3';
|
|
||||||
//
|
|
||||||
scRGBCOLOR: Result := Format('%x%x%x', [ARGBColor.Red div $100, ARGBColor.Green div $100, ARGBColor.Blue div $100]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
{@@
|
{@@
|
||||||
Helper function for the spreadsheet writers.
|
Helper function for the spreadsheet writers.
|
||||||
|
|
||||||
@@ -3812,3 +3623,32 @@ finalization
|
|||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
{ Strategy for handling of number formats:
|
||||||
|
|
||||||
|
Problem:
|
||||||
|
For number formats, fpspreadsheet uses a syntax which is slightly different from
|
||||||
|
the syntax that Excel uses in the xls files. Moreover, the file syntax can be
|
||||||
|
different from file type to file type (biff2, for example, allows only a few
|
||||||
|
predefined formats, while the number of allowed formats is unlimited (?) for
|
||||||
|
biff8.
|
||||||
|
|
||||||
|
Number format handling in fpspreadsheet is implemented with the following
|
||||||
|
concept in mind:
|
||||||
|
|
||||||
|
- Formats written into TsWorksheet cells always follow the fpspreadsheet syntax.
|
||||||
|
The exception is for the format nfCustom for which the format strings are
|
||||||
|
left untouched.
|
||||||
|
|
||||||
|
- For writing, the writer creates a TsNumFormatList which stores all formats
|
||||||
|
in file syntax.
|
||||||
|
- The built-in formats of the file types are coded in the file syntax.
|
||||||
|
- The method "ConvertBeforeWriting" converts the cell formats from the
|
||||||
|
fpspreadsheet to the file syntax.
|
||||||
|
|
||||||
|
- For reading, the reader creates another TsNumFormatList.
|
||||||
|
- The built-in formats of the file types are coded again in file syntax.
|
||||||
|
- The formats read from the file are added in file syntax.
|
||||||
|
- After reading, the formats are converted to fpspreadsheet syntax
|
||||||
|
("ConvertAfterReading").
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -42,9 +42,12 @@ type
|
|||||||
TsBIFF2NumFormatList = class(TsCustomNumFormatList)
|
TsBIFF2NumFormatList = class(TsCustomNumFormatList)
|
||||||
protected
|
protected
|
||||||
procedure AddBuiltinFormats; override;
|
procedure AddBuiltinFormats; override;
|
||||||
|
procedure ConvertBeforeWriting(var AFormatString: String;
|
||||||
|
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String); override;
|
||||||
|
function FindFormatOf(AFormatCell: PCell): Integer; override;
|
||||||
public
|
public
|
||||||
constructor Create(AWorkbook: TsWorkbook);
|
constructor Create(AWorkbook: TsWorkbook);
|
||||||
// function FormatStringForWriting(AIndex: Integer): String; override;
|
function FormatStringForWriting(AIndex: Integer): String; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFF2Reader }
|
{ TsSpreadBIFF2Reader }
|
||||||
@@ -82,7 +85,7 @@ type
|
|||||||
|
|
||||||
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
|
TsSpreadBIFF2Writer = class(TsSpreadBIFFWriter)
|
||||||
private
|
private
|
||||||
function FindXFIndex(ACell: PCell): Word;
|
function FindXFIndex(ACell: PCell): Word;
|
||||||
{ Record writing methods }
|
{ Record writing methods }
|
||||||
procedure WriteBOF(AStream: TStream);
|
procedure WriteBOF(AStream: TStream);
|
||||||
procedure WriteCellFormatting(AStream: TStream; ACell: PCell; XFIndex: Word);
|
procedure WriteCellFormatting(AStream: TStream; ACell: PCell; XFIndex: Word);
|
||||||
@@ -100,7 +103,7 @@ type
|
|||||||
procedure WriteXFRecords(AStream: TStream);
|
procedure WriteXFRecords(AStream: TStream);
|
||||||
protected
|
protected
|
||||||
procedure CreateNumFormatList; override;
|
procedure CreateNumFormatList; override;
|
||||||
procedure FixFormat(ACell: PCell); override;
|
procedure ListAllFormattingStyles; override;
|
||||||
procedure ListAllNumFormats; override;
|
procedure ListAllNumFormats; override;
|
||||||
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override;
|
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override;
|
||||||
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
|
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
|
||||||
@@ -172,19 +175,24 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsBIFF2NumFormatList.AddBuiltinFormats;
|
procedure TsBIFF2NumFormatList.AddBuiltinFormats;
|
||||||
|
var
|
||||||
|
ds, ts, cs: string;
|
||||||
begin
|
begin
|
||||||
|
ds := DefaultFormatSettings.DecimalSeparator;
|
||||||
|
ts := DefaultFormatSettings.ThousandSeparator;
|
||||||
|
cs := DefaultFormatSettings.CurrencyString;
|
||||||
AddFormat( 0, '', nfGeneral);
|
AddFormat( 0, '', nfGeneral);
|
||||||
AddFormat( 1, '0', nfFixed, 0);
|
AddFormat( 1, '0', nfFixed, 0);
|
||||||
AddFormat( 2, '0.00', nfFixed, 2);
|
AddFormat( 2, '0'+ds+'00', nfFixed, 2); // 0.00
|
||||||
AddFormat( 3, '#,##0', nfFixedTh, 0);
|
AddFormat( 3, '#'+ts+'##0', nfFixedTh, 0); // #,##0
|
||||||
AddFormat( 4, '#,##0.00', nfFixedTh, 2);
|
AddFormat( 4, '#'+ts+'##0'+ds+'00', nfFixedTh, 2); // #,##0.00
|
||||||
AddFormat( 5, '"$"#,##0_);("$"#,##0)', nfCurrency, 0);
|
AddFormat( 5, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);("'+cs+'"#'+ts+'##0)'), nfCurrency, 0);
|
||||||
AddFormat( 6, '"$"#,##0_);[Red]("$"#,##0)', nfCurrencyRed, 2);
|
AddFormat( 6, UTF8ToAnsi('"'+cs+'"#'+ts+'##0_);[Red]("'+cs+'"#'+ts+'##0)'), nfCurrencyRed, 2);
|
||||||
AddFormat( 7, '"$"#,##0.00_);("$"#,##0.00)', nfCurrency, 0);
|
AddFormat( 7, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency, 0);
|
||||||
AddFormat( 8, '"$"#,##0.00_);[Red]("$"#,##0.00)', nfCurrency, 2);
|
AddFormat( 8, UTF8ToAnsi('"'+cs+'"#'+ts+'##0'+ds+'00_);[Red]("'+cs+'"#'+ts+'##0'+ds+'00)'), nfCurrency, 2);
|
||||||
AddFormat( 9, '0%', nfPercentage, 0);
|
AddFormat( 9, '0%', nfPercentage, 0);
|
||||||
AddFormat(10, '0.00%', nfPercentage, 2);
|
AddFormat(10, '0'+ds+'00%', nfPercentage, 2);
|
||||||
AddFormat(11, '0.00E+00', nfExp, 2);
|
AddFormat(11, '0'+ds+'00E+00', nfExp, 2);
|
||||||
AddFormat(12, 'M/D/YY', nfShortDate);
|
AddFormat(12, 'M/D/YY', nfShortDate);
|
||||||
AddFormat(13, 'D-MMM-YY', nfLongDate);
|
AddFormat(13, 'D-MMM-YY', nfLongDate);
|
||||||
AddFormat(14, 'D-MMM', nfFmtDateTime);
|
AddFormat(14, 'D-MMM', nfFmtDateTime);
|
||||||
@@ -199,40 +207,126 @@ begin
|
|||||||
FNextFormatIndex := 21; // not needed - there are not user-defined formats
|
FNextFormatIndex := 21; // not needed - there are not user-defined formats
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Creates formatting strings that are written into the file. }
|
|
||||||
(*
|
procedure TsBIFF2NumFormatList.ConvertBeforeWriting(var AFormatString: String;
|
||||||
function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String;
|
var ANumFormat: TsNumberFormat; var ADecimals: Byte; var ACurrencySymbol: String);
|
||||||
var
|
var
|
||||||
ds, ts, cs: string;
|
fmt: String;
|
||||||
begin
|
begin
|
||||||
ds := DefaultFormatSettings.DecimalSeparator;
|
case ANumFormat of
|
||||||
ts := DefaultFormatSettings.ThousandSeparator;
|
nfGeneral:
|
||||||
cs := DefaultFormatSettings.CurrencyString;
|
;
|
||||||
case AIndex of
|
nfFixed, nfFixedTh, nfPercentage, nfExp,
|
||||||
0: Result := 'General';
|
nfCurrency, nfCurrencyRed, nfCurrencyDash, nfCurrencyDashRed:
|
||||||
1: Result := '0';
|
if ADecimals > 0 then ADecimals := 2;
|
||||||
2: Result := '0' + ds + '00'; // 0.00
|
nfSci:
|
||||||
3: Result := '#' + ts + '#0'; // #,##0
|
begin
|
||||||
4: Result := '#' + ts + '#0' + ds + '0'; // #,##0.00
|
if ADecimals > 0 then ADecimals := 2;
|
||||||
5: Result := UTF8ToAnsi(Format('"%s"#%s##0_);("%s"#%s##0)', [cs, ts, cs, ts]));
|
ANumFormat := nfExp;
|
||||||
6: Result := UTF8ToAnsi(Format('"%s"#%s##0_);[Red]("%s"#%s##0)', [cs, ts, cs, ts]));
|
end;
|
||||||
7: Result := UTF8ToAnsi(Format('"%s"#%s##0%s00_);("%s"#%s##0%s00)', [cs, ts, ds, cs, ts, ds]));
|
nfFmtDateTime:
|
||||||
8: Result := UTF8ToAnsi(Format('"%s"#%s##0%s00_);[Red]("%s"#%s##0%s00)', [cs, ts, ds, cs, ts, ds]));
|
begin
|
||||||
9: Result := '0%';
|
fmt := lowercase(AFormatString);
|
||||||
10: Result := '0' + ds + '00%'; // 0.00%
|
if (fmt = 'd-mm') or (fmt = 'd/mm') or
|
||||||
11: Result := '0' + ds + '00E+00'; // 0.00E+00
|
(fmt = 'dd-mm') or (fmt = 'dd/mm') or
|
||||||
12: Result := 'm/d/yy';
|
(fmt = 'dd-mmm') or (fmt = 'dd/mmm')
|
||||||
13: Result := 'd-mmm-yy';
|
then
|
||||||
14: Result := 'd-mmm';
|
AFormatString := 'D-MMM'
|
||||||
15: Result := 'mmm-yy';
|
else
|
||||||
16: Result := 'h:mm AM/PM';
|
if (fmt = 'm-yy') or (fmt = 'm/yy') or
|
||||||
17: Result := 'h:mm:ss AM/PM';
|
(fmt = 'mm-yy') or (fmt = 'mm/yy') or
|
||||||
18: Result := 'h:mm';
|
(fmt = 'mmm-yy') or (fmt = 'mmm/yy') or
|
||||||
19: Result := 'h:mm:ss';
|
(fmt = 'm-yyyy') or (fmt = 'm/yyyy') or
|
||||||
20: Result := 'm/d/yy h:mm';
|
(fmt = 'mm-yyyy') or (fmt = 'mm/yyyy') or
|
||||||
|
(fmt = 'mmm-yyyy') or (fmt = 'mmm-yyyy')
|
||||||
|
then
|
||||||
|
AFormatString := 'MMM-YY'
|
||||||
|
else
|
||||||
|
if (copy(fmt, 1, 5) = 'nn:ss') or (copy(fmt, 1, 5) = 'mm:ss') or
|
||||||
|
(copy(fmt, 1, 4) = 'n:ss') or (copy(fmt, 1, 4) = 'm:ss')
|
||||||
|
then
|
||||||
|
ANumFormat := nfLongTime
|
||||||
|
else
|
||||||
|
ANumFormat := nfShortDateTime;
|
||||||
|
end;
|
||||||
|
nfCustom, nfTimeInterval:
|
||||||
|
begin
|
||||||
|
ANumFormat := nfGeneral;
|
||||||
|
AFormatString := '';
|
||||||
|
ADecimals := 0;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
*)
|
|
||||||
|
|
||||||
|
function TsBIFF2NumFormatList.FindFormatOf(AFormatCell: PCell): Integer;
|
||||||
|
var
|
||||||
|
fmt: String;
|
||||||
|
begin
|
||||||
|
case AFormatCell^.NumberFormat of
|
||||||
|
nfGeneral : Result := 0;
|
||||||
|
nfFixed : Result := IfThen(AFormatCell^.Decimals = 0, 1, 2);
|
||||||
|
nfFixedTh : Result := IfThen(AFormatCell^.Decimals = 0, 3, 4);
|
||||||
|
nfCurrency,
|
||||||
|
nfCurrencyDash : Result := IfThen(AFormatCell^.Decimals = 0, 5, 7);
|
||||||
|
nfCurrencyRed,
|
||||||
|
nfCurrencyDashRed: Result := IfThen(AFormatCell^.Decimals = 0, 6, 8);
|
||||||
|
nfPercentage : Result := IfThen(AFormatCell^.Decimals = 0, 9, 10);
|
||||||
|
nfExp, nfSci : Result := 11;
|
||||||
|
nfShortDate : Result := 12;
|
||||||
|
nfLongDate : Result := 13;
|
||||||
|
nfShortTimeAM : Result := 16;
|
||||||
|
nfLongTimeAM : Result := 17;
|
||||||
|
nfShortTime : Result := 18;
|
||||||
|
nfLongTime : Result := 19;
|
||||||
|
nfShortDateTime : Result := 20;
|
||||||
|
nfFmtDateTime : begin
|
||||||
|
fmt := lowercase(AFormatCell^.NumberFormatStr);
|
||||||
|
if (fmt = 'd-mmm') or (fmt = 'd/mmm') or
|
||||||
|
(fmt = 'd-mm') or (fmt = 'd/mm') or
|
||||||
|
(fmt = 'dd-mm') or (fmt = 'dd/mm') or
|
||||||
|
(fmt = 'dd-mmm') or (fmt = 'dd/mmm')
|
||||||
|
then
|
||||||
|
Result := 14
|
||||||
|
else
|
||||||
|
if (fmt = 'mmm-yy') or (fmt = 'mmm/yy') or
|
||||||
|
(fmt = 'mm-yy') or (fmt = 'mm/yy') or
|
||||||
|
(fmt = 'm-yy') or (fmt = 'm/y') or
|
||||||
|
(fmt = 'mmm-yyyy') or (fmt = 'mmm/yyyy') or
|
||||||
|
(fmt = 'mm-yyyy') or (fmt = 'mm/yyyy') or
|
||||||
|
(fmt = 'm-yyyy') or (fmt = 'm/yyyy')
|
||||||
|
then
|
||||||
|
Result := 15
|
||||||
|
else
|
||||||
|
if (fmt = 'nn:ss') or (fmt = 'mm:ss') or
|
||||||
|
(fmt = 'n:ss') or (fmt = 'm:ss')
|
||||||
|
then
|
||||||
|
Result := 19
|
||||||
|
else
|
||||||
|
if (fmt = 'nn:ss.z') or (fmt = 'mm:ss.z') or
|
||||||
|
(fmt = 'n:ss.z') or (fmt = 'm:ss.z') or
|
||||||
|
(fmt = 'nn:ss.zzz') or (fmt = 'mm:ss.zzz') or
|
||||||
|
(fmt = 'n:ss.zzz') or (fmt = 'm:ss.zzz')
|
||||||
|
then
|
||||||
|
Result := 19
|
||||||
|
else
|
||||||
|
Result := 20;
|
||||||
|
end;
|
||||||
|
nfCustom,
|
||||||
|
nfTimeInterval : Result := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Creates formatting strings that are written into the file. These are the
|
||||||
|
strings in the format list. The only exception is the nfGeneral entry which
|
||||||
|
is written as "General". }
|
||||||
|
function TsBIFF2NumFormatList.FormatStringForWriting(AIndex: Integer): String;
|
||||||
|
begin
|
||||||
|
Result := inherited FormatStringForWriting(AIndex);
|
||||||
|
if Result = '' then
|
||||||
|
Result := 'General';
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFF2Reader }
|
{ TsSpreadBIFF2Reader }
|
||||||
|
|
||||||
@@ -707,57 +801,62 @@ end;
|
|||||||
|
|
||||||
function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word;
|
function TsSpreadBIFF2Writer.FindXFIndex(ACell: PCell): Word;
|
||||||
var
|
var
|
||||||
i: Integer;
|
lIndex: Integer;
|
||||||
|
lCell: TCell;
|
||||||
begin
|
begin
|
||||||
|
// First try the fast methods for default formats
|
||||||
if ACell^.UsedFormattingFields = [] then
|
if ACell^.UsedFormattingFields = [] then
|
||||||
Result := 15
|
Result := 15
|
||||||
else begin
|
else begin
|
||||||
// If not, then we need to search in the list of dynamic formats
|
// If not, then we need to search in the list of dynamic formats
|
||||||
i := 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.
|
||||||
|
// And for BIFF2, there is only a limited number of formats.
|
||||||
|
lCell := ACell^;
|
||||||
|
with lCell do 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;
|
||||||
|
lIndex := FindFormattingInList(@lCell);
|
||||||
|
|
||||||
// Carefully check the index
|
// Carefully check the index
|
||||||
if (i < 0) or (i > Length(FFormattingStyles)) then
|
if (lIndex < 0) or (lIndex > Length(FFormattingStyles)) then
|
||||||
raise Exception.Create('[TsSpreadBIFF2Writer.WriteXFIndex] Invalid Index, this should not happen!');
|
raise Exception.Create('[TsSpreadBIFF2Writer.WriteXFIndex] Invalid Index, this should not happen!');
|
||||||
Result := FFormattingStyles[i].Row;
|
Result := FFormattingStyles[lIndex].Row;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsSpreadBIFF2Writer.FixFormat(ACell: PCell);
|
procedure TsSpreadBIFF2Writer.ListAllFormattingStyles;
|
||||||
var
|
var
|
||||||
j: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
case ACell.NumberFormat of
|
inherited ListAllFormattingStyles;
|
||||||
nfExp:
|
|
||||||
if ACell.Decimals <> 2 then begin
|
for i:=0 to High(FFormattingStyles) do
|
||||||
ACell.Decimals := 2;
|
FNumFormatList.ConvertBeforeWriting(
|
||||||
ACell.CurrencySymbol := '';
|
FFormattingStyles[i].NumberFormatStr,
|
||||||
ACell.NumberFormatStr := '0.00E+00';
|
FFormattingStyles[i].NumberFormat,
|
||||||
end;
|
FFormattingStyles[i].Decimals,
|
||||||
nfSci:
|
FFormattingStyles[i].CurrencySymbol
|
||||||
begin
|
);
|
||||||
ACell.NumberFormat := nfExp;
|
|
||||||
ACell.NumberFormatStr := '0.00E+00';
|
|
||||||
ACell.Decimals := 2;
|
|
||||||
ACell.CurrencySymbol := '';
|
|
||||||
end;
|
|
||||||
nfFmtDateTime:
|
|
||||||
begin
|
|
||||||
j := NumFormatList.Find(ACell);
|
|
||||||
if j = -1 then ACell.NumberFormat := nfLongTime;
|
|
||||||
end;
|
|
||||||
nfCustom:
|
|
||||||
ACell.NumberFormat := nfGeneral;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Builds up the list of number formats to be written to the biff2 file.
|
||||||
|
Unlike biff5+ no formats are added here because biff2 supports only 21
|
||||||
|
standard formats; these formats have been added by the NumFormatList's
|
||||||
|
AddBuiltInFormats. }
|
||||||
procedure TsSpreadBIFF2Writer.ListAllNumFormats;
|
procedure TsSpreadBIFF2Writer.ListAllNumFormats;
|
||||||
begin
|
begin
|
||||||
// Nothing to do. All formats have already been added by the NumFormatList.
|
// Nothing to do here.
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{
|
{ Attaches cell formatting data for the given cell to the current record.
|
||||||
Attaches cell formatting data for the given cell to the current record.
|
Is called from all writing methods of cell contents. }
|
||||||
Is called from all writing methods of cell contents.
|
|
||||||
}
|
|
||||||
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell;
|
procedure TsSpreadBIFF2Writer.WriteCellFormatting(AStream: TStream; ACell: PCell;
|
||||||
XFIndex: Word);
|
XFIndex: Word);
|
||||||
var
|
var
|
||||||
@@ -1002,17 +1101,9 @@ begin
|
|||||||
|
|
||||||
// Now apply the modifications.
|
// Now apply the modifications.
|
||||||
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
||||||
j := NumFormatList.Find(@FFormattingStyles[i]);
|
j := NumFormatList.FindFormatOf(@FFormattingStyles[i]);
|
||||||
if j > -1 then begin
|
if j > -1 then
|
||||||
lFormatIndex := NumFormatList[j].Index;
|
lFormatIndex := NumFormatList[j].Index;
|
||||||
{
|
|
||||||
// BIFF2 can only handle the 21 built-in formats. Here we find replacements
|
|
||||||
// for the others.
|
|
||||||
case NumFormatList[j].NumFormat of
|
|
||||||
nfSci : lFormatIndex := 11; // Exp
|
|
||||||
nfFmtDateTime : if lFormatIndex > 20 then lFormatIndex := 19;
|
|
||||||
end;}
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if uffBorder in FFormattingStyles[i].UsedFormattingFields then
|
if uffBorder in FFormattingStyles[i].UsedFormattingFields then
|
||||||
@@ -1162,26 +1253,6 @@ begin
|
|||||||
AStream.WriteByte(len); // AnsiString, char count in 1 byte
|
AStream.WriteByte(len); // AnsiString, char count in 1 byte
|
||||||
AStream.WriteBuffer(s[1], len); // String data
|
AStream.WriteBuffer(s[1], len); // String data
|
||||||
end;
|
end;
|
||||||
(*
|
|
||||||
procedure TsSpreadBIFF2Writer.WriteFormat(AStream: TStream; AFormatCode: String);
|
|
||||||
var
|
|
||||||
len: Integer;
|
|
||||||
s: AnsiString;
|
|
||||||
begin
|
|
||||||
if AFormatCode = '' then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
s := AFormatCode;
|
|
||||||
len := Length(s);
|
|
||||||
|
|
||||||
{ BIFF record header }
|
|
||||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
|
|
||||||
AStream.WriteWord(WordToLE(len + 1));
|
|
||||||
|
|
||||||
{ Write format string }
|
|
||||||
AStream.WriteByte(len);
|
|
||||||
AStream.WriteBuffer(s[1], len);
|
|
||||||
end; *)
|
|
||||||
|
|
||||||
procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream);
|
procedure TsSpreadBIFF2Writer.WriteFormatCount(AStream: TStream);
|
||||||
begin
|
begin
|
||||||
@@ -1189,83 +1260,6 @@ begin
|
|||||||
AStream.WriteWord(WordToLE(2));
|
AStream.WriteWord(WordToLE(2));
|
||||||
AStream.WriteWord(WordToLE(21)); // there are 21 built-in formats
|
AStream.WriteWord(WordToLE(21)); // there are 21 built-in formats
|
||||||
end;
|
end;
|
||||||
(*
|
|
||||||
procedure TsSpreadBIFF2Writer.WriteFormats(AStream: TStream);
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
WriteFormatCount(AStream);
|
|
||||||
for i:=0 to High(NUMFORMAT_BIFF2) do
|
|
||||||
WriteFormat(AStream, NUMFORMAT_BIFF2[i]);
|
|
||||||
end;*)
|
|
||||||
(*
|
|
||||||
var
|
|
||||||
ds, ts: Char; //decimal separator, thousand separator
|
|
||||||
begin
|
|
||||||
ds := DefaultFormatSettings.DecimalSeparator;
|
|
||||||
ts := DefaultFormatSettings.ThousandSeparator;
|
|
||||||
{ 0} WriteFormat(AStream, 'General'); // 0
|
|
||||||
{ 1} WriteFormat(AStream, '0');
|
|
||||||
{ 2} WriteFormat(AStream, '0'+ds+'00'); // 0.00
|
|
||||||
{ 3} WriteFormat(AStream, '#'+ts+'##0'); // #,##0
|
|
||||||
{ 4} WriteFormat(AStream, '#'+ts+'##0'+ds+'00'); // #,##0.00
|
|
||||||
{ 5} WriteFormat(AStream, '"$"#'+ts+'##0_);("$"#'+ts+'##0)');
|
|
||||||
{ 6} WriteFormat(AStream, '"$"#'+ts+'##0_);[Red]("$"#'+ts+'##0)');
|
|
||||||
{ 7} WriteFormat(AStream, '"$"#'+ts+'##0'+ds+'00_);("$"#'+ts+'##0'+ds+'00)');
|
|
||||||
{ 8} WriteFormat(AStream, '"$"#'+ts+'##0'+ds+'00_);[Red]("$"#'+ts+'##0'+ds+'00)');
|
|
||||||
{ 9} WriteFormat(AStream, '0%');
|
|
||||||
{10} WriteFormat(AStream, '0'+ds+'00%'); // 0.00%
|
|
||||||
{11} WriteFormat(AStream, '0'+ds+'00E+00'); // 0.00E+00
|
|
||||||
{12} WriteFormat(AStream, 'm/d/yy');
|
|
||||||
{13} WriteFormat(AStream, 'd-mmm-yy');
|
|
||||||
{14} WriteFormat(AStream, 'd-mmm');
|
|
||||||
{15} WriteFormat(AStream, 'mmm-yy');
|
|
||||||
{16} WriteFormat(AStream, 'h:mm AM/PM');
|
|
||||||
{17} WriteFormat(AStream, 'h:mm:ss AM/PM');
|
|
||||||
{18} WriteFormat(AStream, 'h:mm');
|
|
||||||
{19} WriteFormat(AStream, 'h:mm:ss');
|
|
||||||
{20} WriteFormat(AStream, 'm/d/yy h:mm');
|
|
||||||
|
|
||||||
{ # TODO: locale support
|
|
||||||
0 => 'GENERAL',
|
|
||||||
1 => '0',
|
|
||||||
2 => '0.00',
|
|
||||||
3 => '#,##0',
|
|
||||||
4 => '#,##0.00',
|
|
||||||
5 => '"$"#,##0_);("$"#,##0)',
|
|
||||||
6 => '"$"#,##0_);[Red]("$"#,##0)',
|
|
||||||
7 => '"$"#,##0.00_);("$"#,##0.00)',
|
|
||||||
8 => '"$"#,##0.00_);[Red]("$"#,##0.00)',
|
|
||||||
9 => '0%',
|
|
||||||
10 => '0.00%',
|
|
||||||
11 => '0.00E+00',
|
|
||||||
12 => '# ?/?',
|
|
||||||
13 => '# ??/??',
|
|
||||||
14 => 'M/D/YY',
|
|
||||||
15 => 'D-MMM-YY',
|
|
||||||
16 => 'D-MMM',
|
|
||||||
17 => 'MMM-YY',
|
|
||||||
18 => 'h:mm AM/PM',
|
|
||||||
19 => 'h:mm:ss AM/PM',
|
|
||||||
20 => 'h:mm',
|
|
||||||
21 => 'h:mm:ss',
|
|
||||||
22 => 'M/D/YY h:mm',
|
|
||||||
37 => '_(#,##0_);(#,##0)',
|
|
||||||
38 => '_(#,##0_);[Red](#,##0)',
|
|
||||||
39 => '_(#,##0.00_);(#,##0.00)',
|
|
||||||
40 => '_(#,##0.00_);[Red](#,##0.00)',
|
|
||||||
41 => '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)',
|
|
||||||
42 => '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)',
|
|
||||||
43 => '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)',
|
|
||||||
44 => '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)',
|
|
||||||
45 => 'mm:ss',
|
|
||||||
46 => '[h]:mm:ss',
|
|
||||||
47 => 'mm:ss.0',
|
|
||||||
48 => '##0.0E+0',
|
|
||||||
49 => '@',
|
|
||||||
}
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Writes an Excel 2 FORMULA record
|
Writes an Excel 2 FORMULA record
|
||||||
|
@@ -1119,7 +1119,7 @@ begin
|
|||||||
|
|
||||||
// Now apply the modifications.
|
// Now apply the modifications.
|
||||||
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
||||||
j := NumFormatList.Find(@FFormattingStyles[i]);
|
j := NumFormatList.FindFormatOf(@FFormattingStyles[i]);
|
||||||
if j > -1 then
|
if j > -1 then
|
||||||
lFormatIndex := NumFormatList[j].Index;
|
lFormatIndex := NumFormatList[j].Index;
|
||||||
end;
|
end;
|
||||||
|
@@ -307,7 +307,7 @@ begin
|
|||||||
|
|
||||||
// Now apply the modifications.
|
// Now apply the modifications.
|
||||||
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
if uffNumberFormat in FFormattingStyles[i].UsedFormattingFields then begin
|
||||||
j := NumFormatList.Find(@FFormattingStyles[i]);
|
j := NumFormatList.FindFormatOf(@FFormattingStyles[i]);
|
||||||
if j > -1 then
|
if j > -1 then
|
||||||
lFormatIndex := NumFormatList[j].Index;
|
lFormatIndex := NumFormatList[j].Index;
|
||||||
end;
|
end;
|
||||||
|
@@ -348,14 +348,7 @@ type
|
|||||||
TsBIFFNumFormatList = class(TsCustomNumFormatList)
|
TsBIFFNumFormatList = class(TsCustomNumFormatList)
|
||||||
protected
|
protected
|
||||||
procedure AddBuiltinFormats; override;
|
procedure AddBuiltinFormats; override;
|
||||||
{
|
|
||||||
procedure ConvertAfterReading(AFormatIndex: Integer; var AFormatString: String;
|
|
||||||
var ANumFormat: TsNumberFormat; var ADecimals: Byte;
|
|
||||||
var ACurrencySymbol: String); override;
|
|
||||||
procedure ConvertBeforeWriting(var AFormatString: String); override;
|
|
||||||
}
|
|
||||||
public
|
public
|
||||||
// function FormatStringForWriting(AIndex: Integer): String; override;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TsSpreadBIFFReader }
|
{ TsSpreadBIFFReader }
|
||||||
@@ -589,138 +582,13 @@ begin
|
|||||||
FFirstFormatIndexInFile := 164;
|
FFirstFormatIndexInFile := 164;
|
||||||
FNextFormatIndex := 164;
|
FNextFormatIndex := 164;
|
||||||
end;
|
end;
|
||||||
(*
|
|
||||||
{ Considers some Excel specialities for format detection.
|
|
||||||
The output values will be passed to fpc. }
|
|
||||||
procedure TsBIFFNumFormatList.Analyze(AFormatIndex: Integer;
|
|
||||||
var AFormatString: String; var ANumFormat: TsNumberFormat;
|
|
||||||
var ADecimals: Byte; var ACurrencySymbol: String);
|
|
||||||
var
|
|
||||||
fmt: String;
|
|
||||||
{
|
|
||||||
parser: TsNumFormatParser;
|
|
||||||
sections: TsNumFormatSections;
|
|
||||||
}
|
|
||||||
begin
|
|
||||||
|
|
||||||
{
|
|
||||||
AFormatString := 'hh:mm:ss.0 AM/PM'; //"€" #,##.0;[red]"$" -#,##.000;-';
|
|
||||||
|
|
||||||
parser := TsNumFormatParser.Create(Workbook, AFormatString);
|
|
||||||
try
|
|
||||||
fmt := parser.FormatString;
|
|
||||||
ANumFormat := parser.ParsedSections[0].NumFormat;
|
|
||||||
ADecimals := parser.ParsedSections[0].Decimals;
|
|
||||||
ACurrencySymbol := parser.ParsedSections[0].CurrencySymbol;
|
|
||||||
parser.CopySectionsTo(sections);
|
|
||||||
finally
|
|
||||||
parser.Free;
|
|
||||||
end;
|
|
||||||
|
|
||||||
parser := TsNumFormatParser.Create(Workbook, sections);
|
|
||||||
try
|
|
||||||
fmt := parser.FormatString;
|
|
||||||
finally
|
|
||||||
parser.Free;
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt := Lowercase(AFormatString);
|
|
||||||
{ Check the built-in formats first:
|
|
||||||
The prefix "[$-F400]" before the formatting string means that the system's
|
|
||||||
long time format string is used. }
|
|
||||||
if (pos('[$-F400]', AFormatString) = 1) then begin
|
|
||||||
ANumFormat := nfLongTime;
|
|
||||||
AFormatString := ''; // will be replaced by system's format setting
|
|
||||||
ADecimals := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
{ Excel often has the locale ID [$-409] (for Germany) in front of the format
|
|
||||||
string. We currently ignore this because it confuses fpc. }
|
|
||||||
if (pos('[$', fmt) = 1) then begin
|
|
||||||
if (pos('h:mm:ss\', fmt) > 0) then begin
|
|
||||||
// long time format
|
|
||||||
if (pos('am/pm', fmt) > 0) or (pos('a/p',fmt) > 0) then begin
|
|
||||||
ANumFormat := nfLongTimeAM;
|
|
||||||
AFormatString := '';
|
|
||||||
end else begin
|
|
||||||
ANumFormat := nfLongTime;
|
|
||||||
AFormatString := '';
|
|
||||||
end;
|
|
||||||
ADecimals := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
if (pos('h:mm\', fmt) > 0) then begin
|
|
||||||
if (pos('am/pm', fmt) > 0) or (pos ('a/p', fmt) > 0) then begin
|
|
||||||
ANumFormat := nfShortTimeAM;
|
|
||||||
AFormatString := '';
|
|
||||||
end else begin
|
|
||||||
ANumFormat := nfShortTime;
|
|
||||||
AFormatString := '';
|
|
||||||
end;
|
|
||||||
ADecimals := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// TO DO: Analyze currency
|
|
||||||
end;
|
|
||||||
|
|
||||||
inherited Analyze(AFormatIndex, AFormatString, ANumFormat, ADecimals, ACurrencySymbol);
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
(*
|
|
||||||
{ Creates formatting strings that are written into the file. }
|
|
||||||
function TsBIFFNumFormatList.FormatStringForWriting(AIndex: Integer): String;
|
|
||||||
var
|
|
||||||
item: TsNumFormatData;
|
|
||||||
i: Integer;
|
|
||||||
|
|
||||||
procedure FixN(var s: String);
|
|
||||||
// The minutes in short time formats are coded by "n" in fpc, but Excel wants "m".
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
for i:=1 to Length(s) do
|
|
||||||
case s[i] of
|
|
||||||
'n', 'N': s[i] := 'm'; // no "M" which will be interpreted as "Month"
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Result := inherited FormatStringForWriting(AIndex);
|
|
||||||
item := Items[AIndex];
|
|
||||||
case item.NumFormat of
|
|
||||||
nfFmtDateTime:
|
|
||||||
begin
|
|
||||||
Result := lowercase(item.FormatString);
|
|
||||||
for i:=1 to Length(Result) do begin
|
|
||||||
// The milliseccond format contains the symbol "z" in fpc, but Excel wants "0"
|
|
||||||
if Result[i] in ['z', 'Z'] then Result[i] := '0';
|
|
||||||
end;
|
|
||||||
FixN(Result);
|
|
||||||
end;
|
|
||||||
nfTimeInterval:
|
|
||||||
begin
|
|
||||||
// Time interval format string could still be without square brackets
|
|
||||||
// if added by user.
|
|
||||||
// We check here for safety and add the brackets if not there.
|
|
||||||
MakeTimeIntervalMask(item.FormatString, Result);
|
|
||||||
FixN(Result);
|
|
||||||
end;
|
|
||||||
nfShortTime, nfShortTimeAM, nfLongTime, nfLongTimeAM:
|
|
||||||
FixN(Result);
|
|
||||||
nfCurrencyRed, nfCurrencyDashRed:
|
|
||||||
begin
|
|
||||||
i := Pos(';', item.FormatString);
|
|
||||||
Result := Copy(item.FormatString, 1, i) + '[RED]'+ Copy(item.FormatString, i+1, Length(item.FormatString));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
*)
|
|
||||||
|
|
||||||
{ TsSpreadBIFFReader }
|
{ TsSpreadBIFFReader }
|
||||||
|
|
||||||
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
|
constructor TsSpreadBIFFReader.Create(AWorkbook: TsWorkbook);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
inherited Create(AWorkbook);
|
inherited Create(AWorkbook);
|
||||||
FXFList := TFPList.Create;
|
FXFList := TFPList.Create;
|
||||||
@@ -793,9 +661,18 @@ end;
|
|||||||
formats.
|
formats.
|
||||||
Valid for BIFF5.BIFF8. Needs to be overridden for BIFF2. }
|
Valid for BIFF5.BIFF8. Needs to be overridden for BIFF2. }
|
||||||
procedure TsSpreadBIFFReader.CreateNumFormatList;
|
procedure TsSpreadBIFFReader.CreateNumFormatList;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
item: TsNumFormatData;
|
||||||
begin
|
begin
|
||||||
FreeAndNil(FNumFormatList);
|
FreeAndNil(FNumFormatList);
|
||||||
FNumFormatList := TsBIFFNumFormatList.Create(Workbook);
|
FNumFormatList := TsBIFFNumFormatList.Create(Workbook);
|
||||||
|
// Convert builtin formats to fps syntax
|
||||||
|
for i:=0 to FNumFormatList.Count-1 do begin
|
||||||
|
item := FNumFormatList[i];
|
||||||
|
FNumFormatList.ConvertAfterReading(item.Index, item.FormatString,
|
||||||
|
item.NumFormat, item.Decimals, item.CurrencySymbol);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Extracts a number out of an RK value.
|
{ Extracts a number out of an RK value.
|
||||||
@@ -1332,7 +1209,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ TsSpreadBIFFWriter }
|
{ TsSpreadBIFFWriter }
|
||||||
|
|
||||||
constructor TsSpreadBIFFWriter.Create(AWorkbook: TsWorkbook);
|
constructor TsSpreadBIFFWriter.Create(AWorkbook: TsWorkbook);
|
||||||
|
Reference in New Issue
Block a user