You've already forked lazarus-ccr
fpspreadsheet: Improved conversion of numbers to formatted strings, handles more special cases (e.g. integer-only fractions, optional digits, optional space digits).
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4094 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -1031,6 +1031,7 @@ begin
|
|||||||
decs := Worksheet.GetDisplayedDecimals(ACell)
|
decs := Worksheet.GetDisplayedDecimals(ACell)
|
||||||
else
|
else
|
||||||
decs := FDecimals;
|
decs := FDecimals;
|
||||||
|
|
||||||
inc(decs, FDelta);
|
inc(decs, FDelta);
|
||||||
if decs < 0 then decs := 0;
|
if decs < 0 then decs := 0;
|
||||||
Worksheet.WriteDecimals(ACell, decs);
|
Worksheet.WriteDecimals(ACell, decs);
|
||||||
|
@ -135,6 +135,7 @@ begin
|
|||||||
FWorkbook := AWorkbook;
|
FWorkbook := AWorkbook;
|
||||||
Parse(AFormatString);
|
Parse(AFormatString);
|
||||||
CheckSections;
|
CheckSections;
|
||||||
|
if AFormatString = '' then FSections[0].NumFormat := nfGeneral;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TsNumFormatParser.Destroy;
|
destructor TsNumFormatParser.Destroy;
|
||||||
@ -1358,12 +1359,19 @@ end;
|
|||||||
procedure TsNumFormatParser.SetDecimals(AValue: Byte);
|
procedure TsNumFormatParser.SetDecimals(AValue: Byte);
|
||||||
var
|
var
|
||||||
i, j, n: Integer;
|
i, j, n: Integer;
|
||||||
|
foundDecs: Boolean;
|
||||||
begin
|
begin
|
||||||
|
foundDecs := false;
|
||||||
for j := 0 to High(FSections) do begin
|
for j := 0 to High(FSections) do begin
|
||||||
n := Length(FSections[j].Elements);
|
n := Length(FSections[j].Elements);
|
||||||
i := n-1;
|
i := n-1;
|
||||||
while (i > -1) do begin
|
while (i > -1) do begin
|
||||||
case FSections[j].Elements[i].Token of
|
case FSections[j].Elements[i].Token of
|
||||||
|
nftDecSep: // this happens, e.g., for "0.E+00"
|
||||||
|
if (AValue > 0) and not foundDecs then begin
|
||||||
|
InsertElement(j, i, nftZeroDecs, AValue);
|
||||||
|
break;
|
||||||
|
end;
|
||||||
nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit, nftIntTh:
|
nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit, nftIntTh:
|
||||||
// no decimals so far --> add decimal separator and decimals element
|
// no decimals so far --> add decimal separator and decimals element
|
||||||
if (AValue > 0) then begin
|
if (AValue > 0) then begin
|
||||||
@ -1373,6 +1381,8 @@ begin
|
|||||||
break;
|
break;
|
||||||
end;
|
end;
|
||||||
nftZeroDecs, nftOptDecs, nftSpaceDecs:
|
nftZeroDecs, nftOptDecs, nftSpaceDecs:
|
||||||
|
begin
|
||||||
|
foundDecs := true;
|
||||||
if AValue > 0 then begin
|
if AValue > 0 then begin
|
||||||
// decimals are already used, just replace value of decimal places
|
// decimals are already used, just replace value of decimal places
|
||||||
FSections[j].Elements[i].IntValue := AValue;
|
FSections[j].Elements[i].IntValue := AValue;
|
||||||
@ -1385,6 +1395,7 @@ begin
|
|||||||
break;
|
break;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
dec(i);
|
dec(i);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
@ -4424,12 +4424,15 @@ var
|
|||||||
numFmt: TsNumFormatParams;
|
numFmt: TsNumFormatParams;
|
||||||
numFmtStr: String;
|
numFmtStr: String;
|
||||||
begin
|
begin
|
||||||
if (ACell = nil) then
|
if (ACell = nil) or (ACell^.ContentType <> cctNumber) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
fmt := FWorkbook.GetCellFormat(ACell^.FormatIndex);
|
fmt := FWorkbook.GetCellFormat(ACell^.FormatIndex);
|
||||||
numFmt := FWorkbook.GetNumberFormat(fmt.NumberFormatIndex);
|
numFmt := FWorkbook.GetNumberFormat(fmt.NumberFormatIndex);
|
||||||
numFmtStr := numFmt.NumFormatStr;
|
if numFmt <> nil then
|
||||||
|
numFmtStr := numFmt.NumFormatStr
|
||||||
|
else
|
||||||
|
numFmtStr := '0.00';
|
||||||
parser := TsNumFormatParser.Create(Workbook, numFmtStr);
|
parser := TsNumFormatParser.Create(Workbook, numFmtStr);
|
||||||
try
|
try
|
||||||
parser.Decimals := ADecimals;
|
parser.Decimals := ADecimals;
|
||||||
|
@ -2400,6 +2400,441 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TsNumFormatTokenSet = set of TsNumFormatToken;
|
||||||
|
|
||||||
|
const
|
||||||
|
TERMINATING_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftSpace, nftText, nftEscaped, nftPercent, nftCurrSymbol, nftSign, nftSignBracket];
|
||||||
|
INT_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit];
|
||||||
|
DECS_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftZeroDecs, nftOptDecs, nftSpaceDecs];
|
||||||
|
FRACNUM_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftFracNumOptDigit, nftFracNumZeroDigit, nftFracNumSpaceDigit];
|
||||||
|
FRACDENOM_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftFracDenomOptDigit, nftFracDenomZeroDigit, nftFracDenomSpaceDigit];
|
||||||
|
EXP_TOKENS: TsNumFormatTokenSet =
|
||||||
|
[nftExpDigits]; // todo: expand by optional digits (0.00E+#)
|
||||||
|
|
||||||
|
{ Checks whether a sequence of format tokens for exponential formatting begins
|
||||||
|
at the specified index in the format elements }
|
||||||
|
function CheckExp(const AElements: TsNumFormatElements; AIndex: Integer): Boolean;
|
||||||
|
var
|
||||||
|
numEl: Integer;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
numEl := Length(AElements);
|
||||||
|
|
||||||
|
Result := (AIndex < numEl) and (AElements[AIndex].Token in INT_TOKENS);
|
||||||
|
if not Result then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
numEl := Length(AElements);
|
||||||
|
i := AIndex + 1;
|
||||||
|
while (i < numEl) and (AElements[i].Token in INT_TOKENS) do inc(i);
|
||||||
|
|
||||||
|
// no decimal places
|
||||||
|
if (i+2 < numEl) and
|
||||||
|
(AElements[i].Token = nftExpChar) and
|
||||||
|
(AElements[i+1].Token = nftExpSign) and
|
||||||
|
(AElements[i+2].Token in EXP_TOKENS)
|
||||||
|
then begin
|
||||||
|
Result := true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// with decimal places
|
||||||
|
if (i < numEl) and (AElements[i].Token = nftDecSep) //and (AElements[i+1].Token in DECS_TOKENS)
|
||||||
|
then begin
|
||||||
|
inc(i);
|
||||||
|
while (i < numEl) and (AElements[i].Token in DECS_TOKENS) do inc(i);
|
||||||
|
if (i + 2 < numEl) and
|
||||||
|
(AElements[i].Token = nftExpChar) and
|
||||||
|
(AElements[i+1].Token = nftExpSign) and
|
||||||
|
(AElements[i+2].Token in EXP_TOKENS)
|
||||||
|
then begin
|
||||||
|
Result := true;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function CheckFraction(const AElements: TsNumFormatElements; AIndex: Integer;
|
||||||
|
out digits: Integer): Boolean;
|
||||||
|
var
|
||||||
|
numEl: Integer;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
digits := 0;
|
||||||
|
numEl := Length(AElements);
|
||||||
|
|
||||||
|
Result := (AIndex < numEl);
|
||||||
|
if not Result then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
i := AIndex;
|
||||||
|
// Check for mixed fraction (integer split off, sample format "# ??/??"
|
||||||
|
if (AElements[i].Token in (INT_TOKENS + [nftIntTh])) then
|
||||||
|
begin
|
||||||
|
inc(i);
|
||||||
|
while (i < numEl) and (AElements[i].Token in (INT_TOKENS + [nftIntTh])) do inc(i);
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do inc(i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (i = numEl) or not (AElements[i].Token in FRACNUM_TOKENS) then
|
||||||
|
exit(false);
|
||||||
|
|
||||||
|
// Here follows the ordinary fraction (no integer split off); sample format "??/??"
|
||||||
|
while (i < numEl) and (AElements[i].Token in FRACNUM_TOKENS) do inc(i);
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do inc(i);
|
||||||
|
if (AElements[i].Token <> nftFracSymbol) then
|
||||||
|
exit(False);
|
||||||
|
|
||||||
|
inc(i);
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do inc(i);
|
||||||
|
if not (AElements[i].Token in FRACDENOM_TOKENS) then
|
||||||
|
exit(false);
|
||||||
|
|
||||||
|
while (i < numEL) and (AElements[i].Token in FRACDENOM_TOKENS) do
|
||||||
|
begin
|
||||||
|
case AElements[i].Token of
|
||||||
|
nftFracDenomZeroDigit : inc(digits, AElements[i].IntValue);
|
||||||
|
nftFracDenomSpaceDigit: inc(digits, AElements[i].IntValue);
|
||||||
|
nftFracDenomOptDigit : inc(digits, AElements[i].IntValue);
|
||||||
|
end;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Processes a sequence of #, 0, and ? tokens.
|
||||||
|
Adds leading (GrowRight=false) or trailing (GrowRight=true) zeros and/or
|
||||||
|
spaces as specified by the format elements to the number value string. }
|
||||||
|
function ProcessIntegerFormat(AValue: String; AFormatSettings: TFormatSettings;
|
||||||
|
const AElements: TsNumFormatElements; var AIndex: Integer;
|
||||||
|
ATokens: TsNumFormatTokenSet; GrowRight, UseThSep: Boolean): String;
|
||||||
|
const
|
||||||
|
OptTokens = [nftIntOptDigit, nftFracNumOptDigit, nftFracDenomOptDigit, nftOptDecs];
|
||||||
|
ZeroTokens = [nftIntZeroDigit, nftFracNumZeroDigit, nftFracDenomZeroDigit, nftZeroDecs, nftIntTh];
|
||||||
|
SpaceTokens = [nftIntSpaceDigit, nftFracNumSpaceDigit, nftFracDenomSpaceDigit, nftSpaceDecs];
|
||||||
|
AllOptTokens = OptTokens + SpaceTokens;
|
||||||
|
var
|
||||||
|
fs: TFormatSettings absolute AFormatSettings;
|
||||||
|
i, j, L: Integer;
|
||||||
|
numEl: Integer;
|
||||||
|
begin
|
||||||
|
Result := AValue;
|
||||||
|
numEl := Length(AElements);
|
||||||
|
if GrowRight then
|
||||||
|
begin
|
||||||
|
// This branch is intended for decimal places, i.e. there may be trailing zeros.
|
||||||
|
i := AIndex;
|
||||||
|
if (AValue = '0') and (AElements[i].Token in AllOptTokens) then
|
||||||
|
Result := '';
|
||||||
|
// Remove trailing zeros
|
||||||
|
while (Result <> '') and (Result[Length(Result)] = '0')
|
||||||
|
do Delete(Result, Length(Result), 1);
|
||||||
|
// Add trailing zeros or spaces as required by the elements.
|
||||||
|
i := AIndex;
|
||||||
|
L := 0;
|
||||||
|
while (i < numEl) and (AElements[i].Token in ATokens) do
|
||||||
|
begin
|
||||||
|
if AElements[i].Token in ZeroTokens then
|
||||||
|
begin
|
||||||
|
inc(L, AElements[i].IntValue);
|
||||||
|
while Length(Result) < L do Result := Result + '0'
|
||||||
|
end else
|
||||||
|
if AElements[i].Token in SpaceTokens then
|
||||||
|
begin
|
||||||
|
inc(L, AElements[i].IntValue);
|
||||||
|
while Length(Result) < L do Result := Result + ' ';
|
||||||
|
end;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
if UseThSep then begin
|
||||||
|
j := 2;
|
||||||
|
while (j < Length(Result)) and (Result[j-1] <> ' ') and (Result[j] <> ' ') do
|
||||||
|
begin
|
||||||
|
Insert(fs.ThousandSeparator, Result, 1);
|
||||||
|
inc(j, 3);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
AIndex := i;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
// This branch is intended for digits (or integer and numerator parts of fractions)
|
||||||
|
// --> There are no leading zeros.
|
||||||
|
// Find last digit token of the sequence
|
||||||
|
i := AIndex;
|
||||||
|
while (i < numEl) and (AElements[i].Token in ATokens) do
|
||||||
|
inc(i);
|
||||||
|
j := i;
|
||||||
|
dec(i);
|
||||||
|
if (AValue = '0') and (AElements[i].Token in AllOptTokens) and (i = AIndex) then
|
||||||
|
Result := '';
|
||||||
|
// From the end of the sequence, going backward, add leading zeros or spaces
|
||||||
|
// as required by the elements of the format.
|
||||||
|
L := 0;
|
||||||
|
while (i >= AIndex) do begin
|
||||||
|
if AElements[i].Token in ZeroTokens then
|
||||||
|
begin
|
||||||
|
inc(L, AElements[i].IntValue);
|
||||||
|
while Length(Result) < L do Result := '0' + Result;
|
||||||
|
end else
|
||||||
|
if AElements[i].Token in SpaceTokens then
|
||||||
|
begin
|
||||||
|
inc(L, AElements[i].IntValue);
|
||||||
|
while Length(Result) < L do Result := ' ' + Result;
|
||||||
|
end;
|
||||||
|
dec(i);
|
||||||
|
end;
|
||||||
|
AIndex := j;
|
||||||
|
if UseThSep then
|
||||||
|
begin
|
||||||
|
j := Length(Result) - 2;
|
||||||
|
while (j > 1) and (Result[j-1] <> ' ') and (Result[j] <> ' ') do
|
||||||
|
begin
|
||||||
|
Insert(fs.ThousandSeparator, Result, j);
|
||||||
|
dec(j, 3);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Converts the floating point number to an exponential number string according
|
||||||
|
to the format specification in AElements.
|
||||||
|
It must have been verified before, that the elements in fact are valid for
|
||||||
|
an exponential format. }
|
||||||
|
function ProcessExpFormat(AValue: Double; AFormatSettings: TFormatSettings;
|
||||||
|
const AElements: TsNumFormatElements; var AIndex: Integer): String;
|
||||||
|
var
|
||||||
|
fs: TFormatSettings absolute AFormatSettings;
|
||||||
|
expchar: String;
|
||||||
|
expSign: String;
|
||||||
|
se, si, sd: String;
|
||||||
|
decs, expDigits: Integer;
|
||||||
|
intZeroDigits, intOptDigits, intSpaceDigits: Integer;
|
||||||
|
numStr: String;
|
||||||
|
i, id, p: Integer;
|
||||||
|
numEl: Integer;
|
||||||
|
begin
|
||||||
|
Result := '';
|
||||||
|
numEl := Length(AElements);
|
||||||
|
|
||||||
|
// Determine digits of integer part of mantissa
|
||||||
|
intZeroDigits := 0;
|
||||||
|
intOptDigits := 0;
|
||||||
|
intSpaceDigits := 0;
|
||||||
|
i := AIndex;
|
||||||
|
while (AElements[i].Token in INT_TOKENS) do begin
|
||||||
|
case AElements[i].Token of
|
||||||
|
nftIntZeroDigit : inc(intZeroDigits, AElements[i].IntValue);
|
||||||
|
nftIntSpaceDigit: inc(intSpaceDigits, AElements[i].IntValue);
|
||||||
|
nftIntOptDigit : inc(intOptDigits, AElements[i].IntValue);
|
||||||
|
end;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// No decimal places
|
||||||
|
if (i + 2 < numEl) and (AElements[i].Token = nftExpChar) then
|
||||||
|
begin
|
||||||
|
expChar := AElements[i].TextValue;
|
||||||
|
expSign := AElements[i+1].TextValue;
|
||||||
|
expDigits := 0;
|
||||||
|
i := i+2;
|
||||||
|
while (i < numEl) and (AElements[i].Token in EXP_TOKENS) do
|
||||||
|
begin
|
||||||
|
inc(expDigits, AElements[i].IntValue); // not exactly what Excel does... Rather exotic case...
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
numstr := FormatFloat('0'+expChar+expSign+DupeString('0', expDigits), AValue, fs);
|
||||||
|
p := pos('e', Lowercase(numStr));
|
||||||
|
se := copy(numStr, p, Length(numStr)); // exp part of the number string, incl "E"
|
||||||
|
numStr := copy(numstr, 1, p-1); // mantissa of the number string
|
||||||
|
numStr := ProcessIntegerFormat(numStr, fs, AElements, AIndex, INT_TOKENS, false, false);
|
||||||
|
Result := numStr + se;
|
||||||
|
AIndex := i;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
// With decimal places
|
||||||
|
if (i + 1 < numEl) and (AElements[i].Token = nftDecSep) then
|
||||||
|
begin
|
||||||
|
inc(i);
|
||||||
|
id := i; // index of decimal elements
|
||||||
|
decs := 0;
|
||||||
|
while (i < numEl) and (AElements[i].Token in DECS_TOKENS) do
|
||||||
|
begin
|
||||||
|
case AElements[i].Token of
|
||||||
|
nftZeroDecs,
|
||||||
|
nftSpaceDecs: inc(decs, AElements[i].IntValue);
|
||||||
|
end;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
expChar := AElements[i].TextValue;
|
||||||
|
expSign := AElements[i+1].TextValue;
|
||||||
|
expDigits := 0;
|
||||||
|
inc(i, 2);
|
||||||
|
while (i < numEl) and (AElements[i].Token in EXP_TOKENS) do
|
||||||
|
begin
|
||||||
|
inc(expDigits, AElements[i].IntValue);
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
if decs=0 then
|
||||||
|
numstr := FormatFloat('0'+expChar+expSign+DupeString('0', expDigits), AValue, fs)
|
||||||
|
else
|
||||||
|
numStr := FloatToStrF(AValue, ffExponent, decs+1, expDigits, fs);
|
||||||
|
if (abs(AValue) >= 1.0) and (expSign = '-') then
|
||||||
|
Delete(numStr, pos('+', numStr), 1);
|
||||||
|
p := pos('e', Lowercase(numStr));
|
||||||
|
se := copy(numStr, p, Length(numStr)); // exp part of the number string, incl "E"
|
||||||
|
numStr := copy(numStr, 1, p-1); // mantissa of the number string
|
||||||
|
p := pos(fs.DecimalSeparator, numStr);
|
||||||
|
if p = 0 then
|
||||||
|
begin
|
||||||
|
si := numstr;
|
||||||
|
sd := '';
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
si := ProcessIntegerFormat(copy(numStr, 1, p-1), fs, AElements, AIndex, INT_TOKENS, false, false); // integer part of the mantissa
|
||||||
|
sd := ProcessIntegerFormat(copy(numStr, p+1, Length(numStr)), fs, AElements, id, DECS_TOKENS, true, false); // fractional part of the mantissa
|
||||||
|
end;
|
||||||
|
// Put all parts together...
|
||||||
|
Result := si + fs.DecimalSeparator + sd + se;
|
||||||
|
AIndex := i;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ProcessFracFormat(AValue: Double; const AFormatSettings: TFormatSettings;
|
||||||
|
ADigits: Integer; const AElements: TsNumFormatElements;
|
||||||
|
var AIndex: Integer): String;
|
||||||
|
var
|
||||||
|
fs: TFormatSettings absolute AFormatSettings;
|
||||||
|
frint, frnum, frdenom, maxdenom: Int64;
|
||||||
|
sfrint, sfrnum, sfrdenom: String;
|
||||||
|
sfrsym, sintnumspace, snumsymspace, ssymdenomspace: String;
|
||||||
|
i, numEl: Integer;
|
||||||
|
mixed: Boolean;
|
||||||
|
begin
|
||||||
|
sintnumspace := '';
|
||||||
|
snumsymspace := '';
|
||||||
|
ssymdenomspace := '';
|
||||||
|
sfrsym := '/';
|
||||||
|
maxDenom := Round(IntPower(10, ADigits));
|
||||||
|
numEl := Length(AElements);
|
||||||
|
|
||||||
|
// Split-off integer
|
||||||
|
i := AIndex;
|
||||||
|
if AElements[i].Token in (INT_TOKENS + [nftIntTh]) then begin
|
||||||
|
mixed := true;
|
||||||
|
if (AValue >= 1) then
|
||||||
|
begin
|
||||||
|
frint := trunc(AValue);
|
||||||
|
AValue := frac(AValue);
|
||||||
|
end else
|
||||||
|
frint := 0;
|
||||||
|
FloatToFraction(AValue, 0.1/maxdenom, MaxInt, maxdenom, frnum, frdenom);
|
||||||
|
sfrint := ProcessIntegerFormat(IntToStr(frint), fs, AElements, i,
|
||||||
|
INT_TOKENS, false, false);
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do
|
||||||
|
begin
|
||||||
|
sintnumspace := sintnumspace + AElements[i].TextValue;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
mixed := false;
|
||||||
|
sfrint := '';
|
||||||
|
FloatToFraction(AValue, 0.1/maxdenom, MaxInt, maxdenom, frnum, frdenom);
|
||||||
|
sintnumspace := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
// "normal" fraction
|
||||||
|
sfrnum := ProcessIntegerFormat(IntToStr(frnum), fs, AElements, i,
|
||||||
|
FRACNUM_TOKENS, false, false);
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do
|
||||||
|
begin
|
||||||
|
snumsymspace := snumsymspace + AElements[i].TextValue;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
inc(i); // fraction symbol
|
||||||
|
while (i < numEl) and (AElements[i].Token in TERMINATING_TOKENS) do
|
||||||
|
begin
|
||||||
|
ssymdenomspace := ssymdenomspace + AElements[i].TextValue;
|
||||||
|
inc(i);
|
||||||
|
end;
|
||||||
|
sfrdenom := ProcessIntegerFormat(IntToStr(frdenom), fs, AElements, i,
|
||||||
|
FRACDENOM_TOKENS, false, false);
|
||||||
|
AIndex := i;
|
||||||
|
|
||||||
|
// Special cases
|
||||||
|
if mixed and (frnum = 0) then
|
||||||
|
begin
|
||||||
|
if sfrnum = '' then begin
|
||||||
|
sintnumspace := '';
|
||||||
|
snumsymspace := '';
|
||||||
|
ssymdenomspace := '';
|
||||||
|
sfrdenom := '';
|
||||||
|
sfrsym := '';
|
||||||
|
end else
|
||||||
|
if trim(sfrnum) = '' then begin
|
||||||
|
sfrdenom := DupeString(' ', Length(sfrdenom));
|
||||||
|
sfrsym := ' ';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if sfrint = '' then sintnumspace := '';
|
||||||
|
|
||||||
|
// Compose result string
|
||||||
|
Result := sfrnum + snumsymspace + sfrsym + ssymdenomspace + sfrdenom;
|
||||||
|
if (Trim(Result) = '') and (sfrint = '') then
|
||||||
|
sfrint := '0';
|
||||||
|
if sfrint <> '' then
|
||||||
|
Result := sfrint + sintnumSpace + result;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ProcessFloatFormat(AValue: Double; AFormatSettings: TFormatSettings;
|
||||||
|
const AElements: TsNumFormatElements; var AIndex: Integer): String;
|
||||||
|
var
|
||||||
|
fs: TFormatSettings absolute AFormatSettings;
|
||||||
|
numEl: Integer;
|
||||||
|
numStr, s: String;
|
||||||
|
p, i: Integer;
|
||||||
|
decs: Integer;
|
||||||
|
useThSep: Boolean;
|
||||||
|
begin
|
||||||
|
Result := '';
|
||||||
|
numEl := Length(AElements);
|
||||||
|
|
||||||
|
// Extract integer part
|
||||||
|
Result := IntToStr(trunc(AValue));
|
||||||
|
useThSep := AElements[AIndex].Token = nftIntTh;
|
||||||
|
Result := ProcessIntegerFormat(Result, fs, AElements, AIndex,
|
||||||
|
(INT_TOKENS + [nftIntTh]), false, UseThSep);
|
||||||
|
|
||||||
|
// Decimals
|
||||||
|
if (AIndex < numEl) and (AElements[AIndex].Token = nftDecSep) then
|
||||||
|
begin
|
||||||
|
inc(AIndex);
|
||||||
|
i := AIndex;
|
||||||
|
// Count decimal digits in format elements
|
||||||
|
decs := 0;
|
||||||
|
while (AIndex < numEl) and (AElements[AIndex].Token in DECS_TOKENS) do begin
|
||||||
|
inc(decs, AElements[AIndex].IntValue);
|
||||||
|
inc(AIndex);
|
||||||
|
end;
|
||||||
|
// Convert value to string
|
||||||
|
numstr := FloatToStrF(AValue, ffFixed, MaxInt, decs, fs);
|
||||||
|
p := Pos(fs.DecimalSeparator, numstr);
|
||||||
|
s := Copy(numstr, p+1, Length(numstr));
|
||||||
|
s := ProcessIntegerFormat(s, fs, AElements, i, DECS_TOKENS, true, false);
|
||||||
|
if s <> '' then
|
||||||
|
Result := Result + fs.DecimalSeparator + s;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Converts a floating point number to a string as determined by the specified
|
Converts a floating point number to a string as determined by the specified
|
||||||
number format parameters
|
number format parameters
|
||||||
@ -2410,115 +2845,11 @@ var
|
|||||||
fs: TFormatSettings absolute AFormatSettings;
|
fs: TFormatSettings absolute AFormatSettings;
|
||||||
sidx: Integer;
|
sidx: Integer;
|
||||||
section: TsNumFormatSection;
|
section: TsNumFormatSection;
|
||||||
i, p, q, el, numEl: Integer;
|
i, el, numEl: Integer;
|
||||||
isNeg: Boolean;
|
isNeg: Boolean;
|
||||||
yr, mon, day, hr, min, sec, ms: Word;
|
yr, mon, day, hr, min, sec, ms: Word;
|
||||||
frInt, frNum, frDenom: Int64;
|
s: String;
|
||||||
maxNum, maxDenom: Int64;
|
digits: Integer;
|
||||||
decsZero, decsOpt, decsSpace: Integer;
|
|
||||||
digitsZero, digitsOpt, digitsSpace: Integer;
|
|
||||||
numDigitsZero, numDigitsOpt, numDigitsSpace: Integer;
|
|
||||||
denomDigitsZero, denomDigitsOpt, denomDigitsSpace: Integer;
|
|
||||||
expSign: Char;
|
|
||||||
expDigits: Integer;
|
|
||||||
numStr, s: String;
|
|
||||||
terminatingTokens: set of TsNumFormatToken;
|
|
||||||
intTokens: set of TsNumFormatToken;
|
|
||||||
decsTokens: set of TsNumFormatToken;
|
|
||||||
fracNumTokens: set of TsNumFormatToken;
|
|
||||||
fracDenomTokens: set of TsNumFormatToken;
|
|
||||||
|
|
||||||
function FixIntPart(AValue: Double; AddThousandSeparator: Boolean;
|
|
||||||
AZeroCount, AOptCount, ASpaceCount: Integer): String;
|
|
||||||
var
|
|
||||||
j: Integer;
|
|
||||||
isNeg: Boolean;
|
|
||||||
begin
|
|
||||||
isNeg := AValue < 0;
|
|
||||||
Result := IntToStr(trunc(abs(AValue)));
|
|
||||||
if (AZeroCount = 0) and (ASpaceCount = 0) then
|
|
||||||
begin
|
|
||||||
if Result = '0' then
|
|
||||||
Result := '';
|
|
||||||
end else
|
|
||||||
if (AZeroCount > 0) and (ASpaceCount = 0) then
|
|
||||||
begin
|
|
||||||
while Length(Result) < AZeroCount do
|
|
||||||
Result := '0' + Result;
|
|
||||||
end else
|
|
||||||
if (AZeroCount = 0) and (ASpaceCount > 0) then
|
|
||||||
begin
|
|
||||||
while Length(Result) < AZeroCount do
|
|
||||||
Result := ' ' + Result;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
while Length(Result) < AZeroCount do
|
|
||||||
Result := '0' + Result;
|
|
||||||
while Length(Result) < AZeroCount + ASpaceCount do
|
|
||||||
Result := ' ' + Result;
|
|
||||||
end;
|
|
||||||
if AddThousandSeparator then
|
|
||||||
begin
|
|
||||||
j := Length(Result)-2;
|
|
||||||
while (j > 1) do
|
|
||||||
begin
|
|
||||||
Insert(fs.ThousandSeparator, Result, j);
|
|
||||||
dec(j, 3);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if isNeg then
|
|
||||||
Result := '-' + Result;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function FixDecimals(AValue: Double;
|
|
||||||
AZeroCount, AOptCount, ASpaceCount: Integer): String;
|
|
||||||
var
|
|
||||||
j, decs: Integer;
|
|
||||||
begin
|
|
||||||
if AZeroCount + AOptCount + ASpaceCount = 0 then
|
|
||||||
begin
|
|
||||||
Result := ''; // no decimals in this case
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := FloatToStrF(abs(frac(AValue)), ffFixed, 20, AZeroCount + AOptCount + ASpaceCount, fs);
|
|
||||||
Delete(Result, 1, 2); // Delete '0.' to extract the decimals
|
|
||||||
decs := Length(Result);
|
|
||||||
while decs < AZeroCount do begin
|
|
||||||
Result := Result + '0';
|
|
||||||
inc(decs);
|
|
||||||
end;
|
|
||||||
|
|
||||||
j := Length(Result);
|
|
||||||
while (Result[j] = '0') and (decs > AZeroCount) and (( decsOpt > 0) or (decsSpace > 0)) do
|
|
||||||
begin
|
|
||||||
if decsOpt > 0 then
|
|
||||||
begin
|
|
||||||
Delete(Result, j, 1);
|
|
||||||
dec(decs);
|
|
||||||
dec(decsOpt);
|
|
||||||
end else
|
|
||||||
if decsSpace > 0 then
|
|
||||||
begin
|
|
||||||
Result[j] := ' ';
|
|
||||||
dec(decs);
|
|
||||||
dec(decsOpt);
|
|
||||||
end;
|
|
||||||
dec(j);
|
|
||||||
end;
|
|
||||||
|
|
||||||
if Result <> '' then
|
|
||||||
Result := fs.DecimalSeparator + Result;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure InvalidFormat;
|
|
||||||
var
|
|
||||||
fmtStr: String;
|
|
||||||
begin
|
|
||||||
fmtStr := AParams.NumFormatStr;
|
|
||||||
raise Exception.CreateFmt(rsIsNoValidNumberFormatString, [fmtStr]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Result := '';
|
Result := '';
|
||||||
if IsNaN(AValue) then
|
if IsNaN(AValue) then
|
||||||
@ -2536,18 +2867,10 @@ begin
|
|||||||
if (AValue = 0) and (Length(AParams.Sections) > 2) then
|
if (AValue = 0) and (Length(AParams.Sections) > 2) then
|
||||||
sidx := 2;
|
sidx := 2;
|
||||||
isNeg := (AValue < 0);
|
isNeg := (AValue < 0);
|
||||||
if (sidx > 0) and isNeg then
|
AValue := abs(AValue); // section 0 adds the sign back, section 1 has the sign in the elements
|
||||||
AValue := -AValue;
|
|
||||||
section := AParams.Sections[sidx];
|
section := AParams.Sections[sidx];
|
||||||
numEl := Length(section.Elements);
|
numEl := Length(section.Elements);
|
||||||
|
|
||||||
terminatingTokens := [nftSpace, nftText, nftPercent, nftCurrSymbol, nftSignBracket,
|
|
||||||
nftEscaped];
|
|
||||||
intTokens := [nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit];
|
|
||||||
decsTokens := [nftZeroDecs, nftOptDecs, nftSpaceDecs];
|
|
||||||
fracNumTokens := [nftFracNumOptDigit, nftFracNumZeroDigit, nftFracNumSpaceDigit];
|
|
||||||
fracDenomTokens := [nftFracDenomOptDigit, nftFracDenomZeroDigit, nftFracDenomSpaceDigit];
|
|
||||||
|
|
||||||
if nfkPercent in section.Kind then
|
if nfkPercent in section.Kind then
|
||||||
AValue := AValue * 100.0;
|
AValue := AValue * 100.0;
|
||||||
if nfkTime in section.Kind then
|
if nfkTime in section.Kind then
|
||||||
@ -2557,306 +2880,29 @@ begin
|
|||||||
|
|
||||||
el := 0;
|
el := 0;
|
||||||
while (el < numEl) do begin
|
while (el < numEl) do begin
|
||||||
|
// Integer token: can be the start of a number, exp, or mixed fraction format
|
||||||
|
// Cases with thousand separator are handled here as well.
|
||||||
|
if section.Elements[el].Token in (INT_TOKENS + [nftIntTh]) then begin
|
||||||
|
// Check for exponential format
|
||||||
|
if CheckExp(section.Elements, el) then
|
||||||
|
s := ProcessExpFormat(AValue, fs, section.Elements, el)
|
||||||
|
else
|
||||||
|
// Check for fraction format
|
||||||
|
if CheckFraction(section.Elements, el, digits) then
|
||||||
|
s := ProcessFracFormat(AValue, fs, digits, section.Elements, el)
|
||||||
|
// Floating-point or integer
|
||||||
|
else
|
||||||
|
s := ProcessFloatFormat(AValue, fs, section.Elements, el);
|
||||||
|
if (sidx = 0) and isNeg then s := '-' + s;
|
||||||
|
Result := Result + s;
|
||||||
|
Continue;
|
||||||
|
end
|
||||||
|
else
|
||||||
case section.Elements[el].Token of
|
case section.Elements[el].Token of
|
||||||
nftIntOptDigit,
|
nftSpace, nftText, nftEscaped, nftCurrSymbol,
|
||||||
nftIntZeroDigit,
|
nftSign, nftSignBracket, nftPercent:
|
||||||
nftIntSpaceDigit:
|
|
||||||
begin
|
|
||||||
// Decimals
|
|
||||||
decsZero := 0;
|
|
||||||
decsSpace := 0;
|
|
||||||
decsOpt := 0;
|
|
||||||
// Integer part of number format
|
|
||||||
digitsZero := 0;
|
|
||||||
digitsSpace := 0;
|
|
||||||
digitsOpt := 0;
|
|
||||||
i := el;
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in intTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftIntOptDigit : inc(digitsOpt, section.Elements[i].IntValue);
|
|
||||||
nftIntZeroDigit : inc(digitsZero, section.Elements[i].IntValue);
|
|
||||||
nftIntSpaceDigit: inc(digitsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ These are the cases that can occur:
|
|
||||||
(1) number w/ decimals ---> end of line
|
|
||||||
(2) number w/ decimals --> space, terminating tokens
|
|
||||||
(3) number w/ decimals --> exponent
|
|
||||||
(4) number w/o decimals --> end of line
|
|
||||||
(5) number w/o decimals --> space, terminating tokens
|
|
||||||
(6) number w/o decimals --> space --> numerator --> '/' --> denominator
|
|
||||||
(7) number w/o decimals --> exponent
|
|
||||||
}
|
|
||||||
// Integer only, followed by end-of-line (case 4)
|
|
||||||
if (i = numEl) or (section.Elements[i].Token in (terminatingTokens - [nftSpace, nftEmptyCharWidth])) then
|
|
||||||
begin
|
|
||||||
Result := Result + FixIntPart(AValue, false, digitsZero, digitsOpt, digitsSpace);
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if (i < numEl) then
|
|
||||||
begin
|
|
||||||
// Check for Exponent (format '0E+00', case 7)
|
|
||||||
if (section.Elements[i].Token = nftExpChar) then begin
|
|
||||||
inc(i);
|
|
||||||
if (i < numEl) and (section.Elements[i].Token = nftExpSign) then begin
|
|
||||||
expSign := section.Elements[i].TextValue[1];
|
|
||||||
inc(i);
|
|
||||||
if (i < numEl) and (section.Elements[i].Token = nftExpDigits) then
|
|
||||||
expDigits := section.Elements[i].IntValue
|
|
||||||
else
|
|
||||||
InvalidFormat;
|
|
||||||
end else
|
|
||||||
InvalidFormat;
|
|
||||||
numStr := FormatFloat('0E'+expSign+DupeString('0', expDigits), AValue, fs);
|
|
||||||
p := pos('e', Lowercase(numStr));
|
|
||||||
s := copy(numStr, p, Length(numStr)); // E part of the number string
|
|
||||||
numStr := copy(numStr, 1, p-1); // Mantissa of the number string
|
|
||||||
Result := Result
|
|
||||||
+ FixIntPart(StrToFloat(numStr, fs), false, digitsZero, digitsOpt, digitsSpace) + s;
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Check for decimal separator
|
|
||||||
if (section.Elements[i].Token = nftDecSep) then
|
|
||||||
begin
|
|
||||||
// Yes, cases (1) or (2) -- get decimal specification
|
|
||||||
decsZero := 0;
|
|
||||||
decsSpace := 0;
|
|
||||||
decsOpt := 0;
|
|
||||||
inc(i);
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in decsTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftZeroDecs : inc(decsZero, section.Elements[i].IntValue);
|
|
||||||
nftOptDecs : inc(decsOpt, section.Elements[i].IntValue);
|
|
||||||
nftSpaceDecs: inc(decsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Simple decimal number (nfFixed), followed by eol (case 1)
|
|
||||||
if (i = numEl) then
|
|
||||||
begin
|
|
||||||
// Simple decimal number (nfFixed) (case 1)
|
|
||||||
Result := Result
|
|
||||||
+ FixIntPart(AValue, false, digitsZero, digitsOpt, digitsSpace)
|
|
||||||
+ FixDecimals(AValue, decsZero, decsOpt, decsSpace);
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Check for exponential format (case 3)
|
|
||||||
if (section.Elements[i].Token = nftExpChar) then
|
|
||||||
begin
|
|
||||||
inc(i);
|
|
||||||
if (i < numEl) and (section.Elements[i].Token = nftExpSign) then begin
|
|
||||||
expSign := section.Elements[i].TextValue[1];
|
|
||||||
inc(i);
|
|
||||||
if (i < numEl) and (section.Elements[i].Token = nftExpDigits) then
|
|
||||||
expDigits := section.Elements[i].IntValue
|
|
||||||
else
|
|
||||||
InvalidFormat;
|
|
||||||
end else
|
|
||||||
InvalidFormat;
|
|
||||||
numStr := FloatToStrF(AValue, ffExponent, decsZero+decsOpt+decsSpace+1, expDigits, fs);
|
|
||||||
if (abs(AValue) >= 1.0) and (expSign = '-') then
|
|
||||||
Delete(numStr, pos('+', numStr), 1);
|
|
||||||
p := pos('e', Lowercase(numStr));
|
|
||||||
s := copy(numStr, p, Length(numStr)); // E part of the number string
|
|
||||||
numStr := copy(numStr, 1, p-1); // Mantissa of the number string
|
|
||||||
q := pos(fs.DecimalSeparator, numStr);
|
|
||||||
Result := Result
|
|
||||||
+ FixIntPart(StrToFloat(numStr, fs), false, digitsZero, digitsOpt, digitsSpace);
|
|
||||||
if q = 0 then
|
|
||||||
Result := Result + s
|
|
||||||
else
|
|
||||||
Result := Result + FixDecimals(StrToFloat(numStr, fs), decsZero, decsOpt, decsSpace) + s;
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Check for fraction (case 6)
|
|
||||||
if (section.Elements[i].Token = nftSpace) or
|
|
||||||
((section.Elements[i].Token = nftText) and (section.Elements[i].TextValue = ' ')) then
|
|
||||||
begin
|
|
||||||
inc(i);
|
|
||||||
if (i < numEl) and (section.Elements[i].Token in fracNumTokens) then
|
|
||||||
begin
|
|
||||||
// Process numerator
|
|
||||||
numDigitsZero := 0;
|
|
||||||
numDigitsSpace := 0;
|
|
||||||
numDigitsOpt := 0;
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in fracNumTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftFracNumOptDigit : inc(numDigitsOpt, section.Elements[i].IntValue);
|
|
||||||
nftFracNumZeroDigit : inc(numDigitsZero, section.Elements[i].IntValue);
|
|
||||||
nftFracNumSpaceDigit: inc(numDigitsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
// Skip spaces before '/' symbol, find '/'
|
|
||||||
while (i < numEl) and (section.Elements[i].Token <> nftFracSymbol) do
|
|
||||||
inc(i);
|
|
||||||
// Skip spaces after '/' symbol, find denominator
|
|
||||||
while (i < numEl) and not (section.Elements[i].Token in fracDenomTokens) do
|
|
||||||
inc(i);
|
|
||||||
// Process denominator
|
|
||||||
denomDigitsZero := 0;
|
|
||||||
denomDigitsOpt := 0;
|
|
||||||
denomDigitsSpace := 0;
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in fracDenomTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftFracDenomOptDigit : inc(denomDigitsOpt, section.Elements[i].IntValue);
|
|
||||||
nftFracDenomZeroDigit : inc(denomDigitsZero, section.Elements[i].IntValue);
|
|
||||||
nftFracDenomSpaceDigit: inc(denomDigitsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Calculate fraction
|
|
||||||
maxNum := Round(IntPower(10, numDigitsOpt+numDigitsZero+numDigitsSpace));
|
|
||||||
maxDenom := Round(IntPower(10, denomDigitsOpt+denomDigitsZero+denomDigitsSpace));
|
|
||||||
if (digitsOpt = 0) and (digitsSpace = 0) and (digitsZero = 0) then
|
|
||||||
begin
|
|
||||||
frint := 0;
|
|
||||||
s := '';
|
|
||||||
end else begin
|
|
||||||
frint := trunc(abs(AValue));
|
|
||||||
AValue := frac(abs(AValue));
|
|
||||||
s := IntToStr(frInt);
|
|
||||||
end;
|
|
||||||
FloatToFraction(abs(AValue), 0.1/maxdenom, maxnum, maxdenom, frnum, frdenom);
|
|
||||||
|
|
||||||
if frInt > 0 then
|
|
||||||
Result := Result +
|
|
||||||
FixIntPart(frInt, false, digitsZero, digitsOpt, digitsSpace);
|
|
||||||
Result := Result +
|
|
||||||
' ' +
|
|
||||||
FixIntPart(frnum, false, numDigitsZero, numDigitsOpt, numDigitsSpace) +
|
|
||||||
'/' +
|
|
||||||
FixIntPart(frdenom, false, denomDigitsZero, denomDigitsOpt, denomDigitsSpace);
|
|
||||||
if isNeg then
|
|
||||||
Result := '-' + Result;
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Simple decimal number (nfFixed), followed by terminating tokens (case 5)
|
|
||||||
if (i < numEl) and (section.Elements[i].Token in terminatingTokens - [nftEmptyCharWidth]) then
|
|
||||||
begin
|
|
||||||
// Simple decimal number (nfFixed) (case 1)
|
|
||||||
Result := Result
|
|
||||||
+ FixIntPart(AValue, false, digitsZero, digitsOpt, digitsSpace)
|
|
||||||
+ FixDecimals(AValue, decsZero, decsOpt, decsSpace);
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
nftIntTh: // Format with thousand separator
|
|
||||||
begin
|
|
||||||
terminatingTokens := [nftSpace, nftText, nftPercent, nftCurrSymbol,
|
|
||||||
nftSignBracket, nftEscaped];
|
|
||||||
decsTokens := [nftZeroDecs, nftOptDecs, nftSpaceDecs];
|
|
||||||
decsZero := 0;
|
|
||||||
decsSpace := 0;
|
|
||||||
decsOpt := 0;
|
|
||||||
digitsZero := section.Elements[el].IntValue;
|
|
||||||
i := el+1;
|
|
||||||
if (i < numEl) and (section.Elements[i].Token = nftDecSep) then
|
|
||||||
begin
|
|
||||||
inc(i);
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in [nftZeroDecs, nftOptDecs, nftSpaceDecs]) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftZeroDecs : inc(decsZero, section.Elements[i].IntValue);
|
|
||||||
nftOptDecs : inc(decsOpt, section.Elements[i].IntValue);
|
|
||||||
nftSpaceDecs: inc(decsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Result := Result + FixIntPart(AValue, true, digitsZero, 0, 0)
|
|
||||||
+ FixDecimals(AValue, decsZero, DecsOpt, decsSpace);
|
|
||||||
el := i;
|
|
||||||
Continue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
nftFracNumZeroDigit,
|
|
||||||
nftFracNumOptDigit,
|
|
||||||
nftFracNumSpaceDigit:
|
|
||||||
begin
|
|
||||||
// Process numerator
|
|
||||||
numDigitsZero := 0;
|
|
||||||
numDigitsSpace := 0;
|
|
||||||
numDigitsOpt := 0;
|
|
||||||
i := el;
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in fracNumTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftFracNumOptDigit : inc(numDigitsOpt, section.Elements[i].IntValue);
|
|
||||||
nftFracNumZeroDigit : inc(numDigitsZero, section.Elements[i].IntValue);
|
|
||||||
nftFracNumSpaceDigit: inc(numDigitsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
// Skip spaces before '/' symbol, find '/'
|
|
||||||
while (i < numEl) and (section.Elements[i].Token <> nftFracSymbol) do
|
|
||||||
inc(i);
|
|
||||||
// Skip spaces after '/' symbol, find denominator
|
|
||||||
while (i < numEl) and not (section.Elements[i].Token in fracDenomTokens) do
|
|
||||||
inc(i);
|
|
||||||
// Process denominator
|
|
||||||
denomDigitsZero := 0;
|
|
||||||
denomDigitsOpt := 0;
|
|
||||||
denomDigitsSpace := 0;
|
|
||||||
while (i < numEl) and (section.Elements[i].Token in fracDenomTokens) do
|
|
||||||
begin
|
|
||||||
case section.Elements[i].Token of
|
|
||||||
nftFracDenomOptDigit : inc(denomDigitsOpt, section.Elements[i].IntValue);
|
|
||||||
nftFracDenomZeroDigit : inc(denomDigitsZero, section.Elements[i].IntValue);
|
|
||||||
nftFracDenomSpaceDigit: inc(denomDigitsSpace, section.Elements[i].IntValue);
|
|
||||||
end;
|
|
||||||
inc(i);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Calculate fraction
|
|
||||||
maxNum := Round(IntPower(10, numDigitsOpt+numDigitsZero+numDigitsSpace));
|
|
||||||
maxDenom := Round(IntPower(10, denomDigitsOpt+denomDigitsZero+denomDigitsSpace));
|
|
||||||
FloatToFraction(abs(AValue), 0.1/maxdenom, MaxInt, maxdenom, frnum, frdenom);
|
|
||||||
if isNeg then
|
|
||||||
Result := Result + '-';
|
|
||||||
Result := Result +
|
|
||||||
FixIntPart(frnum, false, numDigitsZero, numDigitsOpt, numDigitsSpace) +
|
|
||||||
'/' +
|
|
||||||
FixIntPart(frdenom, false, denomDigitsZero, denomDigitsOpt, denomDigitsSpace);
|
|
||||||
el := i-1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
nftSpace:
|
|
||||||
Result := Result + ' ';
|
|
||||||
|
|
||||||
nftText:
|
|
||||||
Result := Result + section.Elements[el].TextValue;
|
Result := Result + section.Elements[el].TextValue;
|
||||||
|
|
||||||
nftEscaped:
|
|
||||||
begin
|
|
||||||
inc(el);
|
|
||||||
if el < Length(section.Elements) then
|
|
||||||
Result := Result + section.Elements[el].TextValue;
|
|
||||||
end;
|
|
||||||
|
|
||||||
nftEmptyCharWidth:
|
nftEmptyCharWidth:
|
||||||
Result := Result + ' ';
|
Result := Result + ' ';
|
||||||
|
|
||||||
@ -2873,12 +2919,6 @@ begin
|
|||||||
nftThSep:
|
nftThSep:
|
||||||
Result := Result + fs.ThousandSeparator;
|
Result := Result + fs.ThousandSeparator;
|
||||||
|
|
||||||
nftSign, nftSignBracket, nftCurrSymbol:
|
|
||||||
Result := Result + section.Elements[el].TextValue;
|
|
||||||
|
|
||||||
nftPercent:
|
|
||||||
Result := Result + '%';
|
|
||||||
|
|
||||||
nftYear:
|
nftYear:
|
||||||
case section.Elements[el].IntValue of
|
case section.Elements[el].IntValue of
|
||||||
1,
|
1,
|
||||||
@ -2963,9 +3003,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
Result := Result + s;
|
Result := Result + s;
|
||||||
end;
|
end;
|
||||||
end;
|
end; // case
|
||||||
inc(el);
|
inc(el);
|
||||||
end;
|
end; // while
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user