diff --git a/components/fpspreadsheet/fpsmath.pas b/components/fpspreadsheet/fpsmath.pas index aa5393f37..f4b62bef1 100644 --- a/components/fpspreadsheet/fpsmath.pas +++ b/components/fpspreadsheet/fpsmath.pas @@ -107,14 +107,22 @@ function fpsNOT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsOR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTRUE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; { String functions } +function fpsCHAR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCODE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsLEFT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsLOWER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsMID (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsPROPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsREPLACE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsRIGHT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsSUBSTITUTE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsTRIM (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsUPPER (Args: TsArgumentStack; NumArgs: Integer): TsArgument; implementation uses - Math; + Math, lazutf8, StrUtils, fpsUtils; type TBoolArray = array of boolean; @@ -736,6 +744,7 @@ begin end; function fpsLOG(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// LOG( number, [base] ) var arg_base, arg_number: TsArgument; data: TFloatArray; @@ -869,6 +878,7 @@ end; function fpsAVEDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument; // Average value of absolute deviations of data from their mean. +// AVEDEV( argument1, [argument2, ... argument_n] ) var data: TFloatArray; m: Double; @@ -884,6 +894,7 @@ begin end; function fpsAVERAGE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// AVERAGE( argument1, [argument2, ... argument_n] ) var data: TFloatArray; begin @@ -892,7 +903,9 @@ begin end; function fpsCOUNT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; -{ Count non-missing arguments } +{ Count non-missing arguments + COUNT( argument1, [argument2, ... argument_n] ) +} var n, i: Integer; begin @@ -903,6 +916,7 @@ begin end; function fpsMAX(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// MAX( number1, number2, ... number_n ) var data: TFloatArray; begin @@ -911,6 +925,7 @@ begin end; function fpsMIN(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// MIN( number1, number2, ... number_n ) var data: TFloatArray; begin @@ -919,6 +934,7 @@ begin end; function fpsPRODUCT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// PRODUCT( number1, number2, ... number_n ) var data: TFloatArray; i: Integer; @@ -933,6 +949,7 @@ begin end; function fpsSTDEV(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// STDEV( number1, [number2, ... number_n] ) var data: TFloatArray; begin @@ -941,6 +958,7 @@ begin end; function fpsSTDEVP(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// STDEVP( number1, [number2, ... number_n] ) var data: TFloatArray; begin @@ -949,6 +967,7 @@ begin end; function fpsSUM(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// SUM( value1, [value2, ... value_n] ) var data: TFloatArray; begin @@ -957,6 +976,7 @@ begin end; function fpsSUMSQ(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// SUMSQ( value1, [value2, ... value_n] ) var data: TFloatArray; begin @@ -965,6 +985,7 @@ begin end; function fpsVAR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// VAR( number1, number2, ... number_n ) var data: TFloatArray; begin @@ -973,6 +994,7 @@ begin end; function fpsVARP(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// VARP( number1, number2, ... number_n ) var data: TFloatArray; begin @@ -984,6 +1006,7 @@ end; { Logical functions } function fpsAND(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// AND( condition1, [condition2], ... ) var data: TBoolArray; i: Integer; @@ -1002,11 +1025,13 @@ begin end; function fpsFALSE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// FALSE( ) begin Result := CreateBool(false); end; function fpsIF(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// IF( condition, [value_if_true], [value_if_false] ) var condition: TsArgument; case1, case2: TsArgument; @@ -1026,6 +1051,7 @@ begin end; function fpsNOT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// NOT( logical_value ) var data: TBoolArray; begin @@ -1034,6 +1060,7 @@ begin end; function fpsOR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// OR( condition1, [condition2], ... ) var data: TBoolArray; i: Integer; @@ -1052,6 +1079,7 @@ begin end; function fpsTRUE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// TRUE ( ) begin Result := CreateBool(true); end; @@ -1059,28 +1087,191 @@ end; { String functions } +function fpsCHAR(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// CHAR( ascii_value ) +var + data: TFloatArray; +begin + if PopFloatValues(Args, 1, data, Result) then begin + if (data[0] >= 0) and (data[0] <= 255) then + Result := CreateString(AnsiToUTF8(Char(Round(data[0])))); + end; +end; + +function fpsCODE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// CODE( text ) +var + data: TStrArray; +begin + if PopStringValues(Args, 1, data, Result) then begin + if Length(data) > 0 then + Result := CreateNumber(Ord(UTF8ToAnsi(data[0])[1])) + else + Result := CreateEmpty; + end; +end; + +function fpsLEFT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// LEFT( text, [number_of_characters] ) +var + arg1, arg2: TsArgument; + count: Integer; + s: String; +begin + count := 1; + if NumArgs = 2 then begin + arg2 := Args.Pop; + if not arg2.IsMissing then begin + if arg2.ArgumentType <> atNumber then begin + Result := createError(errWrongType); + exit; + end; + count := Round(arg2.NumberValue); + end; + end; + arg1 := Args.Pop; + if arg1.ArgumentType <> atString then begin + Result := CreateError(errWrongType); + exit; + end; + s := arg1.StringValue; + Result := CreateString(UTF8LeftStr(s, count)); +end; + function fpsLOWER(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// LOWER( text ) var data: TStrArray; begin if PopStringValues(Args, NumArgs, data, Result) then - Result := CreateString(lowercase(data[0])); + Result := CreateString(UTF8LowerCase(data[0])); +end; + +function fpsMID(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// MID( text, start_position, number_of_characters ) +var + fdata: TFloatArray; + sdata: TStrArray; +begin + if PopFloatValues(Args, 2, fdata, Result) then + if PopStringValues(Args, 1, sdata, Result) then + Result := CreateString(UTF8Copy(sdata[0], Round(fData[0]), Round(fdata[1]))); +end; + +function fpsPROPER(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// PROPER( text ) +var + data: TStrArray; +begin + if PopStringValues(Args, 1, data, Result) then + Result := CreateString(UTF8ProperCase(data[0])); +end; + +function fpsREPLACE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// REPLACE( old_text, start, number_of_chars, new_text ) +var + arg_new, arg_old, arg_start, arg_count: TsArgument; + s, s1, s2, snew: String; + p, count: Integer; +begin + arg_new := Args.Pop; + if arg_new.ArgumentType <> atString then begin + Result := CreateError(errWrongType); + exit; + end; + arg_count := Args.Pop; + if arg_count.ArgumentType <> atNumber then begin + Result := CreateError(errWrongType); + exit; + end; + arg_start := Args.Pop; + if arg_start.ArgumentType <> atNumber then begin + Result := CreateError(errWrongType); + exit; + end; + arg_old := Args.Pop; + if arg_old.ArgumentType <> atString then begin + Result := CreateError(errWrongType); + exit; + end; + + s := arg_old.StringValue; + snew := arg_new.StringValue; + p := round(arg_start.NumberValue); + count := round(arg_count.NumberValue); + + s1 := UTF8Copy(s, 1, p-1); + s2 := UTF8Copy(s, p+count, UTF8Length(s)); + Result := CreateString(s1 + snew + s2); +end; + +function fpsRIGHT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// RIGHT( text, [number_of_characters] ) +var + arg1, arg2: TsArgument; + count: Integer; + s: String; +begin + count := 1; + if NumArgs = 2 then begin + arg2 := Args.Pop; + if not arg2.IsMissing then begin + if arg2.ArgumentType <> atNumber then begin + Result := createError(errWrongType); + exit; + end; + count := round(arg2.NumberValue); + end; + end; + arg1 := Args.Pop; + if arg1.ArgumentType <> atString then begin + Result := CreateError(errWrongType); + exit; + end; + s := arg1.StringValue; + Result := CreateString(UTF8RightStr(s, count)); +end; + +function fpsSUBSTITUTE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// SUBSTITUTE( text, old_text, new_text, [nth_appearance] ) +var + data: TStrArray; + arg_n: TsArgument; + n: Integer; +begin + n := -1; + if NumArgs = 4 then begin + arg_n := Args.Pop; + if arg_n.ArgumentType <> atNumber then begin + Result := CreateError(errWrongType); + exit; + end; + n := round(arg_n.Numbervalue); + end; + if PopStringValues(Args, 3, data, Result) then begin + if n = -1 then + Result := CreateString(UTF8StringReplace(data[0], data[1], data[2], [rfReplaceall])) + else + raise Exception.Create('not implemented'); + end; end; function fpsTRIM(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// TRIM( text ) var data: TStrArray; begin if PopStringValues(Args, NumArgs, data, Result) then - Result := CreateString(trim(data[0])); + Result := CreateString(UTF8Trim(data[0])); end; function fpsUPPER(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// UPPER( text ) var data: TStrArray; begin if PopStringValues(Args, NumArgs, data, Result) then - Result := CreateString(uppercase(data[0])); + Result := CreateString(UTF8UpperCase(data[0])); end; end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index e9049283a..bcda50b29 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1202,15 +1202,15 @@ const (Symbol:'OR'; MinParams:1; MaxParams:30; Func:fpsOR), // fekOR (Symbol:'TRUE'; MinParams:0; MaxParams:0; Func:fpsTRUE), // fekTRUE { string } - (Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:nil), // fekCHAR - (Symbol:'CODE'; MinParams:1; MaxParams:1; Func:nil), // fekCODE - (Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:nil), // fekLEFT + (Symbol:'CHAR'; MinParams:1; MaxParams:1; Func:fpsCHAR), // fekCHAR + (Symbol:'CODE'; MinParams:1; MaxParams:1; Func:fpsCODE), // fekCODE + (Symbol:'LEFT'; MinParams:1; MaxParams:2; Func:fpsLEFT), // fekLEFT (Symbol:'LOWER'; MinParams:1; MaxParams:1; Func:fpsLOWER), // fekLOWER - (Symbol:'MID'; MinParams:3; MaxParams:3; Func:nil), // fekMID - (Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:nil), // fekPROPER - (Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:nil), // fekREPLACE - (Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:nil), // fekRIGHT - (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:nil), // fekSUBSTITUTE + (Symbol:'MID'; MinParams:3; MaxParams:3; Func:fpsMID), // fekMID + (Symbol:'PROPER'; MinParams:1; MaxParams:1; Func:fpsPROPER), // fekPROPER + (Symbol:'REPLACE'; MinParams:4; MaxParams:4; Func:fpsREPLACE), // fekREPLACE + (Symbol:'RIGHT'; MinParams:1; MaxParams:2; Func:fpsRIGHT), // fekRIGHT + (Symbol:'SUBSTITUTE';MinParams:3; MaxParams:4; Func:fpsSUBSTITUTE), // fekSUBSTITUTE (Symbol:'TRIM'; MinParams:1; MaxParams:1; Func:fpsTRIM), // fekTRIM (Symbol:'UPPER'; MinParams:1; MaxParams:1; Func:fpsUPPER), // fekUPPER { lookup/reference } diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 8608eae17..7e4743692 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -77,6 +77,7 @@ function GetCellRangeString(ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelF function GetErrorValueStr(AErrorValue: TsErrorValue): String; +function UTF8ProperCase(AText: String): String; function UTF8TextToXMLText(AText: ansistring): ansistring; function IfThen(ACondition: Boolean; AValue1,AValue2: TsNumberFormat): TsNumberFormat; overload; @@ -139,7 +140,7 @@ var implementation uses - Math; + Math, lazutf8; {******************************************************************************} { Endianess helper functions } @@ -662,6 +663,42 @@ begin end; end; +{@@ + Converts a string to "proper case", i.e. 1st character of each word is + upper-case, other characters are lowercase. + @param S String to be converted + @return String in proper case } +function UTF8ProperCase(AText: String): String; +begin + result := ''; +end; + (* +const + Delims: TSysCharSet = ['0'..'9', ' ', '.', ',', ';', ':', '-', '_', + '<', '>', '|', '''', '#', '*', '+', '~', '!', '"', '§', '$', '%', '&', '/', '(', ')', '=', + '?', '\', '}', ']', '[', '{', '@']; +var + ch: String; + len, i, j: Integer; + res: string; + w: String; + p: Integer; + words: Array of String; +begin + AText := UTF8Lowercase(AText); + + i := 0; + w := 'dummy'; + Result := ''; + while w <> '' do begin + w := ExtractWordPos(i, AText, Delims, p); + Result := UTF8Copy(AText, 1, p-1) + UTF8Uppercase(UTF8Copy(w, 1, 1)) + + UTF8Copy(w, 2, Length(w)-1); + inc(i); + end; +end; + *) + {@@ Converts a string encoded in UTF8 to a string usable in XML. For this purpose, some characters must be translated. diff --git a/components/fpspreadsheet/tests/formulatests.pas b/components/fpspreadsheet/tests/formulatests.pas index c2a6893a5..ac49f3169 100644 --- a/components/fpspreadsheet/tests/formulatests.pas +++ b/components/fpspreadsheet/tests/formulatests.pas @@ -49,7 +49,7 @@ type implementation uses - math, typinfo, fpsUtils, rpnFormulaUnit; + math, typinfo, lazUTF8, fpsUtils, rpnFormulaUnit; { TSpreadWriteReadFormatTests } diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index f9bb72a93..86230b550 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -47,9 +47,6 @@ - - - @@ -129,7 +126,6 @@ - @@ -139,7 +135,6 @@ - @@ -159,9 +154,6 @@ - - - diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index d3222d12f..4c33ba4c6 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -1045,6 +1045,63 @@ { String functions } {------------------------------------------------------------------------------} + // CHAR + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=CHAR(72)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(72, + RPNFunc(fekCHAR, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(char(72)); + + // CODE + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=CODE("Hallo word")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNFunc(fekCODE, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumber(ord('H')); + + // LEFT (2 parameters) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word", 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNNumber(2, + RPNFunc(fekLEFT, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('Ha'); + + // LEFT (2 parameters, utf8) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Ändern", 3)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Ändern', + RPNNumber(3, + RPNFunc(fekLEFT, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('Änd'); + + // LEFT (2 parameters, 1 of them missing) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word", )'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNMissingArg( + RPNFunc(fekLEFT, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('H'); + + // LEFT (1 parameter) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LEFT("Hallo word")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNFunc(fekLEFT, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('H'); + // Lower case inc(Row); MyWorksheet.WriteUTF8Text(Row, 0, '=LOWER("Hallo word")'); @@ -1054,6 +1111,115 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateString(LowerCase('Hallo world')); + // Lower case / utf8 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=LOWER("Viele Grüße")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Viele Grüße', + RPNFunc(fekLOWER, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(UTF8LowerCase('Viele Grüße')); + + // MID + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=MID("Hallo word", 3, 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNNumber(3, + RPNNumber(2, + RPNFunc(fekMID, nil)))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('ll'); + + // REPLACE + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=REPLACE("weather", 2, 2, "he")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('weather', + RPNNumber(2, + RPNNumber(2, + RPNString('he', + RPNFunc(fekREPLACE, nil))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('whether'); + + // REPLACE / utf8 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=REPLACE("würde", 2, 1, "u")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('würde', + RPNNumber(2, + RPNNumber(1, + RPNString('u', + RPNFunc(fekREPLACE, nil))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('wurde'); + +// RIGHT (2 parameters) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word", 2)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNNumber(2, + RPNFunc(fekRIGHT, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('ld'); + + // RIGHT (2 parameters, one of them missing) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word", )'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNMissingArg( + RPNFunc(fekRIGHT, 2, nil))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('d'); + + // RIGHT (1 parameter) + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=RIGHT("Hallo word")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Hallo world', + RPNFunc(fekRIGHT, 1, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('d'); + + // SUBSTITUTE + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=SUBSTITUTE("lazarus", "s", "s.exe")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('lazarus', + RPNString('s', + RPNString('s.exe', + RPNFunc(fekSUBSTITUTE, 3, nil)))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('lazarus.exe'); + + // SUBSTITUTE + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=SUBSTITUTE("lAzArus", "A", "a")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('lAzArus', + RPNString('A', + RPNString('a', + RPNFunc(fekSUBSTITUTE, 3, nil)))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('lazarus'); + + // SUBSTITUTE / utf8 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=SUBSTITUTE("lÄzÄrus", "Ä", "a")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('lÄzÄrus', + RPNString('Ä', + RPNString('a', + RPNFunc(fekSUBSTITUTE, 3, nil)))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString('lazarus'); + + // SUBSTITUTE / 4 parameters + // to do... + // Trim inc(Row); MyWorksheet.WriteUTF8Text(Row, 0, '=TRIM(" Hallo word ")'); @@ -1061,7 +1227,7 @@ RPNString(' Hallo world ', RPNFunc(fekTRIM, nil)))); SetLength(sollValues, Row+1); - sollValues[Row] := CreateString(Trim(' Hallo world ')); + sollValues[Row] := CreateString(UTF8Trim(' Hallo world ')); // Upper case inc(Row); @@ -1072,3 +1238,12 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateString(UpperCase('Hallo world')); + // Upper case / utf8 + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=UPPER("Viele Grüße")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNString('Viele Grüße', + RPNFunc(fekUPPER, nil)))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateString(UTF8UpperCase('Viele Grüße')); +