fpspreadsheet: Fix reading of ods files containing formulas with several arguments (separator semicolon). Update unit tests. (Still issues with files posted at https://forum.lazarus.freepascal.org/index.php/topic,42168.msg293792.html).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6574 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-08-09 14:28:17 +00:00
parent a3d2a3ae8d
commit 6e2cc3fe7e
8 changed files with 182 additions and 111 deletions

View File

@ -687,6 +687,7 @@ type
FDialect: TsFormulaDialect; FDialect: TsFormulaDialect;
FSourceCell: PCell; FSourceCell: PCell;
FDestCell: PCell; FDestCell: PCell;
FListSep: Char;
procedure CheckEOF; procedure CheckEOF;
function GetAsBoolean: Boolean; function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime; function GetAsDateTime: TDateTime;
@ -703,13 +704,14 @@ type
FFormatSettings: TFormatSettings; FFormatSettings: TFormatSettings;
FContains3DRef: Boolean; FContains3DRef: Boolean;
class function BuiltinExpressionManager: TsBuiltInExpressionManager; class function BuiltinExpressionManager: TsBuiltInExpressionManager;
function BuildStringFormula(AFormatSettings: TFormatSettings): String; function BuildStringFormula: String;
procedure ParserError(Msg: String); procedure ParserError(Msg: String);
function GetExpression: String; function GetExpression: String;
function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual; function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual;
procedure SetExpression(const AValue: String); procedure SetExpression(const AValue: String);
procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings; procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings;
const AValue: String); virtual; const AValue: String); virtual;
procedure UpdateExprFormatSettings;
procedure CheckResultType(const Res: TsExpressionResult; procedure CheckResultType(const Res: TsExpressionResult;
AType: TsResultType); inline; AType: TsResultType); inline;
@ -750,6 +752,7 @@ type
property AsDateTime: TDateTime read GetAsDateTime; property AsDateTime: TDateTime read GetAsDateTime;
// The expression to parse // The expression to parse
property Expression: String read GetExpression write SetExpression; property Expression: String read GetExpression write SetExpression;
property ListSeparator: Char read FListSep;
property LocalizedExpression[AFormatSettings: TFormatSettings]: String property LocalizedExpression[AFormatSettings: TFormatSettings]: String
read GetLocalizedExpression write SetLocalizedExpression; read GetLocalizedExpression write SetLocalizedExpression;
property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula; property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula;
@ -847,6 +850,7 @@ procedure RegisterFunction(const AName: ShortString; const AResultType: Char;
const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload; const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload;
var var
// Format settings used in stored parsed formulas.
ExprFormatSettings: TFormatSettings; ExprFormatSettings: TFormatSettings;
const const
@ -1060,7 +1064,7 @@ begin
Result := ttLessThanEqual; Result := ttLessThanEqual;
end end
else else
if D = FParser.FFormatSettings.ListSeparator then if D = FParser.ListSeparator then
Result := ttListSep Result := ttListSep
else else
case D of case D of
@ -1316,7 +1320,7 @@ end;
function TsExpressionScanner.IsDelim(C: Char): Boolean; function TsExpressionScanner.IsDelim(C: Char): Boolean;
begin begin
Result := (C in Delimiters) or (C = FParser.FFormatSettings.ListSeparator); Result := (C in Delimiters) or (C = FParser.ListSeparator);
end; end;
function TsExpressionScanner.IsDigit(C: Char): Boolean; function TsExpressionScanner.IsDigit(C: Char): Boolean;
@ -1326,7 +1330,7 @@ end;
function TsExpressionScanner.IsWordDelim(C: Char): Boolean; function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
begin begin
Result := (C in WordDelimiters) or (C = FParser.FFormatSettings.ListSeparator); Result := (C in WordDelimiters) or (C = FParser.ListSeparator);
end; end;
function TsExpressionScanner.NextPos: Char; function TsExpressionScanner.NextPos: Char;
@ -1368,12 +1372,14 @@ end;
constructor TsExpressionParser.Create(AWorksheet: TsBasicWorksheet); constructor TsExpressionParser.Create(AWorksheet: TsBasicWorksheet);
begin begin
inherited Create; inherited Create;
FDialect := fdExcelA1;
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef); FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef);
FIdentifiers.FParser := Self; FIdentifiers.FParser := Self;
FScanner := TsExpressionScanner.Create(self); FScanner := TsExpressionScanner.Create(self);
FHashList := TFPHashObjectList.Create(False); FHashList := TFPHashObjectList.Create(False);
SetDialect(fdExcelA1);
FFormatSettings := InitFormatSettings(TsWorksheet(FWorksheet).Workbook);
UpdateExprFormatSettings;
end; end;
destructor TsExpressionParser.Destroy; destructor TsExpressionParser.Destroy;
@ -1386,17 +1392,14 @@ begin
end; end;
{ Constructs the string formula from the tree of expression nodes. Gets the { Constructs the string formula from the tree of expression nodes. Gets the
decimal and list separator from the formatsettings provided. } decimal and list separator from current formatsettings. }
function TsExpressionParser.BuildStringFormula(AFormatSettings: TFormatSettings): String; function TsExpressionParser.BuildStringFormula: String;
begin begin
ExprFormatSettings := AFormatSettings; // ExprFormatSettings := AFormatSettings;
if FExprNode = nil then if FExprNode = nil then
Result := '' Result := ''
else else
begin
FFormatSettings := AFormatSettings;
Result := FExprNode.AsString; Result := FExprNode.AsString;
end;
end; end;
class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager; class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
@ -1485,6 +1488,8 @@ begin
end; end;
procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult); procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult);
var
fs: TFormatSettings;
begin begin
{ // Not needed. May be missing after copying formulas { // Not needed. May be missing after copying formulas
if (FExpression = '') then if (FExpression = '') then
@ -1492,7 +1497,15 @@ begin
} }
if not Assigned(FExprNode) then if not Assigned(FExprNode) then
ParserError(rsErrorInExpression); ParserError(rsErrorInExpression);
FExprNode.GetNodeValue(AResult); fs := ExprFormatSettings;
try
UpdateExprFormatSettings;
// ExprFormatSettings := FFormatSettings;
// FFormatSettings := ExprFormatSettings;
FExprNode.GetNodeValue(AResult);
finally
ExprFormatSettings := fs;
end;
end; end;
function TsExpressionParser.GetAsBoolean: Boolean; function TsExpressionParser.GetAsBoolean: Boolean;
@ -1915,19 +1928,16 @@ begin
end; end;
function TsExpressionParser.GetExpression: String; function TsExpressionParser.GetExpression: String;
var
fs: TFormatsettings;
begin begin
fs := DefaultFormatSettings; Result := BuildStringFormula; //(FFormatSettings);
fs.DecimalSeparator := '.';
fs.ListSeparator := ',';
Result := BuildStringFormula(fs);
end; end;
function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String;
begin begin
ExprFormatSettings := AFormatSettings; // ExprFormatSettings := AFormatSettings;
Result := BuildStringFormula(AFormatSettings); FFormatSettings := AFormatSettings;
UpdateExprFormatSettings;
Result := BuildStringFormula; //(AFormatSettings);
end; end;
function TsExpressionParser.Has3DLinks: Boolean; function TsExpressionParser.Has3DLinks: Boolean;
@ -1944,26 +1954,17 @@ end;
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect); procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
begin begin
if FDialect = AValue then exit;
FDialect := AValue; FDialect := AValue;
{ case FDialect of
if FScanner <> nil then fdExcelA1,
case FDialect of fdExcelR1C1 : FListSep := ',';
fdExcelA1, fdExcelR1C1: FScanner.SheetNameTerminator := '!'; fdOpenDocument : FListSep := ';'
fdOpenDocument: FScanner.Sheetnameterminator := '.'; end;
else raise Exception.Create('TsExpressionParser.SetDialect: Dialect not supported.');
end;
}
end; end;
procedure TsExpressionParser.SetExpression(const AValue: String); procedure TsExpressionParser.SetExpression(const AValue: String);
var
fs: TFormatSettings;
begin begin
fs := DefaultFormatSettings; SetLocalizedExpression(FFormatSettings, AValue);
fs.DecimalSeparator := '.';
fs.ListSeparator := ',';
SetLocalizedExpression(fs, AValue);
end; end;
procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings; procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings;
@ -1972,7 +1973,8 @@ begin
if FExpression = AValue then if FExpression = AValue then
exit; exit;
FFormatSettings := AFormatSettings; FFormatSettings := AFormatSettings;
ExprFormatSettings := AFormatSettings; UpdateExprFormatSettings;
// ExprFormatSettings := AFormatSettings;
FExpression := AValue; FExpression := AValue;
if (AValue <> '') and (AValue[1] = '=') then if (AValue <> '') and (AValue[1] = '=') then
FScanner.Source := Copy(AValue, 2, Length(AValue)) FScanner.Source := Copy(AValue, 2, Length(AValue))
@ -2182,6 +2184,16 @@ begin
Result := FScanner.TokenType; Result := FScanner.TokenType;
end; end;
procedure TsExpressionParser.UpdateExprFormatSettings;
var
book: TsWorkbook;
begin
book := TsWorksheet(FWorksheet).Workbook;
ExprFormatSettings.ShortDateFormat := book.FormatSettings.ShortDateFormat;
ExprFormatSettings.ShortTimeFormat := book.FormatSettings.ShortTimeFormat;
ExprFormatSettings.LongTimeFormat := book.FormatSettings.LongTimeFormat;
end;
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
{ TsSpreadsheetParser } { TsSpreadsheetParser }
@ -3738,7 +3750,7 @@ begin
for i := 0 to Length(FArgumentNodes)-1 do for i := 0 to Length(FArgumentNodes)-1 do
begin begin
if (S <> '') then if (S <> '') then
S := S + Parser.FFormatSettings.ListSeparator; S := S + Parser.ListSeparator;
if Assigned(FArgumentNodes[i]) then if Assigned(FArgumentNodes[i]) then
S := S + FArgumentNodes[i].AsString; S := S + FArgumentNodes[i].AsString;
end; end;
@ -4431,8 +4443,9 @@ begin
rtBoolean : if Arg.ResBoolean then Result := 1.0; rtBoolean : if Arg.ResBoolean then Result := 1.0;
rtHyperlink, rtHyperlink,
rtString : begin rtString : begin
fs := ExprFormatSettings; //(Arg.Worksheet as TsWorksheet).Workbook.FormatSettings; fs := ExprFormatSettings;
TryStrToDateTime(ArgToString(Arg), Result, fs); if not TryStrToDateTime(ArgToString(Arg), Result, fs) then
Result := NaN;
end; end;
rtCell : begin rtCell : begin
cell := ArgToCell(Arg); cell := ArgToCell(Arg);
@ -4696,14 +4709,13 @@ begin
end; end;
initialization initialization
ExprFormatSettings := DefaultFormatSettings; // These are the format settings used in storage of parsed formulas.
ExprFormatSettings.DecimalSeparator := '.'; ExprFormatSettings := InitFormatSettings(nil);
ExprFormatSettings.ListSeparator := ','; {
ExprFormatSettings.DateSeparator := '/';
ExprFormatSettings.TimeSeparator := ':';
ExprFormatSettings.ShortDateFormat := 'yyyy/m/d'; // the parser returns single digits ExprFormatSettings.ShortDateFormat := 'yyyy/m/d'; // the parser returns single digits
ExprFormatSettings.LongTimeFormat := 'h:n:s'; ExprFormatSettings.LongTimeFormat := 'h:n:s';
ExprFormatSettings.ShortTimeFormat := 'h:n'; ExprFormatSettings.ShortTimeFormat := 'h:n';
}
RegisterStdBuiltins(BuiltinIdentifiers); RegisterStdBuiltins(BuiltinIdentifiers);

View File

@ -391,6 +391,10 @@ var
begin begin
start_date := ArgToDateTime(Args[0]); start_date := ArgToDateTime(Args[0]);
end_date := ArgToDateTime(Args[1]); end_date := ArgToDateTime(Args[1]);
if IsNaN(start_date) or IsNaN(end_date) then begin
Result := ErrorResult(errWrongType);
exit;
end;
interval := ArgToString(Args[2]); interval := ArgToString(Args[2]);
if end_date > start_date then if end_date > start_date then
@ -428,8 +432,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeDate(dt, y, m, d); if not IsNaN(dt) then begin
Result := IntegerResult(d); DecodeDate(dt, y, m, d);
Result := IntegerResult(d);
end;
end; end;
end; end;
@ -444,8 +450,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeTime(dt, h, m, s, ms); if not IsNaN(dt) then begin
Result := IntegerResult(h); DecodeTime(dt, h, m, s, ms);
Result := IntegerResult(h);
end;
end; end;
end; end;
@ -459,8 +467,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeTime(dt, h, m, s, ms); if not IsNaN(dt) then begin
Result := IntegerResult(m); DecodeTime(dt, h, m, s, ms);
Result := IntegerResult(m);
end;
end; end;
end; end;
@ -474,8 +484,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeDate(dt, y, m, d); if not IsNaN(dt) then begin
Result := IntegerResult(m); DecodeDate(dt, y, m, d);
Result := IntegerResult(m);
end;
end; end;
end; end;
@ -498,8 +510,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeTime(dt, h, m, s, ms); if not IsNaN(dt) then begin
Result := IntegerResult(s); DecodeTime(dt, h, m, s, ms);
Result := IntegerResult(s);
end;
end; end;
end; end;
@ -542,19 +556,17 @@ var
dow: Integer; dow: Integer;
dt: TDateTime; dt: TDateTime;
begin begin
Result := ErrorResult(errWrongType);
if Length(Args) = 2 then if Length(Args) = 2 then
n := ArgToInt(Args[1]) n := ArgToInt(Args[1])
else else
n := 1; n := 1;
if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell] then dt := NaN;
dt := ArgToDateTime(Args[0]) if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell, rtString] then
else dt := ArgToDateTime(Args[0]);
if Args[0].ResultType in [rtString] then if IsNaN(dt) then
if not TryStrToDate(Args[0].ResString, dt) then exit;
begin
Result := ErrorResult(errWrongType);
exit;
end;
dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7 dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7
case n of case n of
1: ; 1: ;
@ -574,8 +586,10 @@ begin
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
begin begin
dt := ArgToDateTime(Args[0]); dt := ArgToDateTime(Args[0]);
DecodeDate(dt, y, m, d); if not IsNaN(dt) then begin
Result := IntegerResult(y); DecodeDate(dt, y, m, d);
Result := IntegerResult(y);
end;
end; end;
end; end;

View File

@ -2400,6 +2400,7 @@ var
p: Integer; p: Integer;
fmt: PsCellFormat; fmt: PsCellFormat;
ns: String; ns: String;
hasFormula: Boolean;
begin begin
{$IFDEF FPSpreadDebug} {$IFDEF FPSpreadDebug}
DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex])); DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex]));
@ -2445,7 +2446,10 @@ begin
formula^.Parser.Expression := formulaStr; formula^.Parser.Expression := formulaStr;
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
formula^.Text := formula^.Parser.Expression; formula^.Text := formula^.Parser.Expression;
{ cell^.Flags := cell^.Flags + [cfHasFormula];
hasFormula := true;
{
cell^.FormulaValue := formula; cell^.FormulaValue := formula;
// Note: This formula is still in OpenDocument dialect. Conversion to // Note: This formula is still in OpenDocument dialect. Conversion to
// ExcelA1 dialect (used by fps) is postponed until all sheets have beeon // ExcelA1 dialect (used by fps) is postponed until all sheets have beeon
@ -2455,9 +2459,12 @@ begin
{$IFDEF FPSpreadDebug} {$IFDEF FPSpreadDebug}
DebugLn(' Formula found: ' + formula); DebugLn(' Formula found: ' + formula);
{$ENDIF} {$ENDIF}
end; end else
hasFormula := false;
// Read formula results // Read formula results
if hasFormula then TsWorkbook(FWorkbook).LockFormulas;
valueType := GetAttrValue(ACellNode, 'office:value-type'); valueType := GetAttrValue(ACellNode, 'office:value-type');
valueStr := GetAttrValue(ACellNode, 'office:value'); valueStr := GetAttrValue(ACellNode, 'office:value');
calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type'); calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type');
@ -2520,6 +2527,8 @@ begin
if (valueStr <> '') then if (valueStr <> '') then
TsWorksheet(FWorksheet).WriteText(cell, valueStr); TsWorksheet(FWorksheet).WriteText(cell, valueStr);
if hasFormula then TsWorkbook(FWorkbook).UnlockFormulas;
if FIsVirtualMode then if FIsVirtualMode then
TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell); TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell);
end; end;

View File

@ -4793,7 +4793,7 @@ end;
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double); procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double);
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula // Delete any pre-existing formula, but only if FormulaLock is ON.
DeleteFormula(ACell); DeleteFormula(ACell);
// Write number to cell // Write number to cell
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;

View File

@ -1018,13 +1018,35 @@ const
HEADER_FOOTER_INDEX_EVEN = 2; HEADER_FOOTER_INDEX_EVEN = 2;
HEADER_FOOTER_INDEX_ALL = 1; HEADER_FOOTER_INDEX_ALL = 1;
var procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings);
{@@ FPC format settings for which all strings have been converted to UTF8 }
UTF8FormatSettings: TFormatSettings;
implementation implementation
{@@ ----------------------------------------------------------------------------
Creates a localized FPC format settings record in which all strings are
encoded as UTF8.
-------------------------------------------------------------------------------}
procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings);
// remove when available in LazUtils
var
i: Integer;
begin
AFormatSettings := DefaultFormatSettings;
AFormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString);
for i:=1 to 12 do begin
AFormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]);
AFormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]);
end;
for i:=1 to 7 do begin
AFormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]);
AFormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]);
end;
end;
{ TsFont }
constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles; constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles;
AColor: TsColor; APosition: TsFontPosition); AColor: TsColor; APosition: TsFontPosition);
begin begin
@ -1078,7 +1100,7 @@ end;
constructor TsBasicWorkbook.Create; constructor TsBasicWorkbook.Create;
begin begin
inherited; inherited;
FormatSettings := UTF8FormatSettings; InitUTF8FormatSettings(FormatSettings);
FUnits := suMillimeters; // Units for column width and row height FUnits := suMillimeters; // Units for column width and row height
FFormatID := sfidUnknown; FFormatID := sfidUnknown;
FLog := TStringList.Create; FLog := TStringList.Create;
@ -1138,31 +1160,5 @@ begin
end; end;
{@@ ----------------------------------------------------------------------------
Creates a FPC format settings record in which all strings are encoded as
UTF8.
-------------------------------------------------------------------------------}
procedure InitUTF8FormatSettings;
// remove when available in LazUtils
var
i: Integer;
begin
UTF8FormatSettings := DefaultFormatSettings;
UTF8FormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString);
for i:=1 to 12 do begin
UTF8FormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]);
UTF8FormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]);
end;
for i:=1 to 7 do begin
UTF8FormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]);
UTF8FormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]);
end;
end;
initialization
InitUTF8FormatSettings;
end. end.

View File

@ -202,6 +202,7 @@ procedure InitCell(AWorksheet: TsBasicWorksheet; ARow, ACol: Cardinal;
out ACell: TCell); overload; out ACell: TCell); overload;
procedure InitCryptoInfo(out AValue: TsCryptoInfo); procedure InitCryptoInfo(out AValue: TsCryptoInfo);
procedure InitFormatRecord(out AValue: TsCellFormat); procedure InitFormatRecord(out AValue: TsCellFormat);
function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings;
procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal; procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal;
AOffsetX, AOffsetY, AScaleX, AScaleY: Double); AOffsetX, AOffsetY, AScaleX, AScaleY: Double);
procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage); procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage);
@ -2439,6 +2440,25 @@ begin
// NOTE: Cell protection is effective only after protecting a worksheet // NOTE: Cell protection is effective only after protecting a worksheet
end; end;
{@@ ----------------------------------------------------------------------------
Initializes the FormatSettingsRecord to international (non-localized) values
-------------------------------------------------------------------------------}
function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings;
begin
Result := DefaultFormatSettings;
Result.DecimalSeparator := '.';
Result.ThousandSeparator := ',';
Result.ListSeparator := ',';
if AWorkbook <> nil then
with AWorkbook.FormatSettings do begin
Result.DateSeparator := DateSeparator;
Result.TimeSeparator := TimeSeparator;
Result.ShortDateFormat := ShortDateFormat; //'yyyy/m/d'; // the parser returns single digits
Result.LongTimeFormat := LongTimeFormat; //'h:n:s';
Result.ShortTimeFormat := ShortTimeFormat; //'h:n';
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Initializes the fields of a TsImage record Initializes the fields of a TsImage record

View File

@ -83,6 +83,10 @@ type
procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML; procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
procedure SumMultiSheetRange_FlippedSheetsAndCells_ODS; procedure SumMultiSheetRange_FlippedSheetsAndCells_ODS;
procedure IfConst_BIFF8;
procedure IfConst_OOXML;
procedure IfConst_ODS;
procedure CountIfRange_BIFF8; procedure CountIfRange_BIFF8;
procedure CountIfRangeSheet_BIFF8; procedure CountIfRangeSheet_BIFF8;
@ -335,7 +339,7 @@ procedure TSpreadSingleFormulaTests.YearConst_BIFF8;
var var
s: String; s: String;
begin begin
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings);
TestFormula(Format('YEAR("%s")', [s]), '2012', ftkConstants, sfExcel8); TestFormula(Format('YEAR("%s")', [s]), '2012', ftkConstants, sfExcel8);
end; end;
@ -348,7 +352,7 @@ procedure TSpreadSingleFormulaTests.MonthConst_BIFF8;
var var
s: String; s: String;
begin begin
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings);
TestFormula(Format('MONTH("%s")', [s]), '2', ftkConstants, sfExcel8); TestFormula(Format('MONTH("%s")', [s]), '2', ftkConstants, sfExcel8);
end; end;
@ -361,7 +365,7 @@ procedure TSpreadSingleFormulaTests.DayConst_BIFF8;
var var
s: String; s: String;
begin begin
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings); s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings);
TestFormula(Format('DAY("%s")', [s]), '5', ftkConstants, sfExcel8); TestFormula(Format('DAY("%s")', [s]), '5', ftkConstants, sfExcel8);
end; end;
@ -376,7 +380,7 @@ procedure TSpreadSingleFormulaTests.HourConst_BIFF8;
var var
s: String; s: String;
begin begin
s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), ExprFormatSettings); s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), DefaultFormatSettings);
TestFormula(Format('HOUR("%s")', [s]), '14', ftkConstants, sfExcel8); TestFormula(Format('HOUR("%s")', [s]), '14', ftkConstants, sfExcel8);
end; end;
@ -515,6 +519,23 @@ end;
{ --- } { --- }
procedure TSpreadSingleFormulaTests.IfConst_BIFF8;
begin
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfExcel8);
end;
procedure TSpreadSingleFormulaTests.IfConst_OOXML;
begin
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOOXML);
end;
procedure TSpreadSingleFormulaTests.IfConst_ODS;
begin
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOpenDocument);
end;
{ --- }
procedure TSpreadSingleFormulaTests.CountIfRange_BIFF8; procedure TSpreadSingleFormulaTests.CountIfRange_BIFF8;
begin begin
TestFormula('COUNTIF(C3:C5,">1")', '1', ftkCellRange, sfExcel8); TestFormula('COUNTIF(C3:C5,">1")', '1', ftkCellRange, sfExcel8);

View File

@ -17,7 +17,6 @@
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
// Addition // Addition
Row := 0; Row := 0;
formula := '1+1'; formula := '1+1';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -1874,7 +1873,7 @@
// DAY / argument string // DAY / argument string
inc(Row); inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
formula := 'DAY("'+s+'")'; formula := 'DAY("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -1906,7 +1905,7 @@
// HOUR / argument string // HOUR / argument string
inc(Row); inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
formula := 'HOUR("'+s+'")'; formula := 'HOUR("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -1938,7 +1937,7 @@
// MINUTE / argument string // MINUTE / argument string
inc(Row); inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
formula := 'MINUTE("'+s+'")'; formula := 'MINUTE("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -1970,7 +1969,7 @@
// MONTH / argument string // MONTH / argument string
inc(Row); inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
formula := 'MONTH("'+s+'")'; formula := 'MONTH("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -2022,7 +2021,7 @@
// SECOND / argument string // SECOND / argument string
inc(Row); inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
formula := 'SECOND("'+s+'")'; formula := 'SECOND("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -2054,7 +2053,7 @@
// TIMEVALUE // TIMEVALUE
inc(Row); inc(Row);
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization! s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization!
formula := 'TIMEVALUE("'+s+'")'; formula := 'TIMEVALUE("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -2104,7 +2103,7 @@
// WEEKDAY / argument string // WEEKDAY / argument string
inc(Row); inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
formula := 'WEEKDAY("'+s+'")'; formula := 'WEEKDAY("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then
@ -2136,7 +2135,7 @@
// YEAR / argument string // YEAR / argument string
inc(Row); inc(Row);
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
formula := 'YEAR("'+s+'")'; formula := 'YEAR("'+s+'")';
MyWorksheet.WriteText(Row, 0, formula); MyWorksheet.WriteText(Row, 0, formula);
if UseRPNFormula then if UseRPNFormula then