diff --git a/components/fpspreadsheet/fpsnumformatparser.pas b/components/fpspreadsheet/fpsnumformatparser.pas
index d191adb18..abd92eb75 100644
--- a/components/fpspreadsheet/fpsnumformatparser.pas
+++ b/components/fpspreadsheet/fpsnumformatparser.pas
@@ -31,8 +31,11 @@ type
nftSign, nftSignBracket,
nftDigit, nftOptDigit, nftOptSpaceDigit, nftDecs, nftOptDec,
nftExpChar, nftExpSign, nftExpDigits,
- nftPercent,
- nftFraction,
+ nftPercent, // '%'
+ nftFracSymbol, // '/'
+ nftFracIntDigit, nftFracIntSpaceDigit, nftFracIntZeroDigit, // '#', '?', '0'
+ nftFracNumDigit, nftFracNumSpaceDigit, nftFracNumZeroDigit,
+ nftFracDenomDigit, nftFracDenomSpaceDigit, nftFracDenomZeroDigit,
nftCurrSymbol, nftCountry,
nftColor, nftCompareOp, nftCompareValue,
nftSpace, nftEscaped,
@@ -119,16 +122,21 @@ type
// General
procedure CheckSections;
procedure CheckSection(ASection: Integer);
+ procedure FixMonthMinuteToken(ASection: Integer);
// Format string
function BuildFormatString(ADialect: TsNumFormatDialect): String; virtual;
function BuildFormatStringFromSection(ASection: Integer;
ADialect: TsNumFormatDialect): String; virtual;
// NumberFormat
procedure EvalNumFormatOfSection(ASection: Integer);
+ function GetTokenIntValueAt(AToken: TsNumFormatToken;
+ ASection,AIndex: Integer): Integer;
function IsCurrencyAt(ASection: Integer; out ANumFormat: TsNumberFormat;
out ADecimals: byte; out ACurrencySymbol: String; out AColor: TsColor): Boolean;
function IsDateAt(ASection,AIndex: Integer; out ANumberFormat: TsNumberFormat;
var ANextIndex: Integer): Boolean;
+ function IsFractionAt(ASection,AIndex: Integer;
+ out AIntPart, ANumPart, ADenomPart, ANextIndex: Integer): Boolean;
function IsNumberAt(ASection,AIndex: Integer; out ANumberFormat: TsNumberFormat;
out ADecimals: Byte; out ANextIndex: Integer): Boolean;
function IsTextAt(AText: string; ASection, AIndex: Integer): Boolean;
@@ -308,12 +316,14 @@ begin
if element.TextValue <> '' then result := Result + '"' + element.TextValue + '"';
nftThSep, nftDecSep:
Result := Result + element.TextValue;
- nftDigit:
- Result := Result + '0';
- nftOptDigit, nftOptDec:
- Result := Result + '#';
- nftOptSpaceDigit:
- Result := Result + '#'; /// !!!!!!!!!!! TO BE CHANGED !!!!!!!!!!!!!!!!!
+ nftDigit, nftFracIntZeroDigit, nftFracNumZeroDigit, nftFracDenomZeroDigit:
+ Result := Result + DupeString('0', element.IntValue);
+ nftOptDigit, nftOptDec, nftFracIntDigit, nftFracNumDigit, nftFracDenomDigit:
+ Result := Result + DupeString('#', element.IntValue);
+ nftOptSpaceDigit, nftFracIntSpaceDigit, nftFracNumSpaceDigit, nftFracDenomSpaceDigit:
+ Result := Result + DupeString('?', element.IntValue);
+ nftFracSymbol:
+ Result := Result + '/';
nftYear:
Result := Result + DupeString(IfThen(ADialect = nfdExcel, 'Y', 'y'), element.IntValue);
nftMonth:
@@ -335,7 +345,7 @@ begin
nftDecs, nftExpDigits, nftMilliseconds:
Result := Result + Dupestring('0', element.IntValue);
nftSpace, nftSign, nftSignBracket, nftExpChar, nftExpSign, nftPercent,
- nftFraction, nftAMPM, nftDateTimeSep:
+ nftAMPM, nftDateTimeSep:
if element.TextValue <> '' then Result := Result + element.TextValue;
nftCurrSymbol:
if element.TextValue <> '' then begin
@@ -383,6 +393,37 @@ begin
end;
procedure TsNumFormatParser.CheckSection(ASection: Integer);
+begin
+ FixMonthMinuteToken(ASection);
+ EvalNumFormatOfSection(ASection);
+end;
+
+procedure TsNumFormatParser.ClearAll;
+var
+ i, j: Integer;
+begin
+ for i:=0 to Length(FSections)-1 do begin
+ for j:=0 to Length(FSections[i].Elements) do
+ if FSections[i].Elements <> nil then
+ FSections[i].Elements[j].TextValue := '';
+ FSections[i].Elements := nil;
+ FSections[i].CurrencySymbol := '';
+ end;
+ FSections := nil;
+end;
+
+procedure TsNumFormatParser.DeleteElement(ASection, AIndex: Integer);
+var
+ i, n: Integer;
+begin
+ n := Length(FSections[ASection].Elements);
+ for i:= AIndex+1 to n-1 do
+ FSections[ASection].Elements[i-1] := FSections[ASection].Elements[i];
+ SetLength(FSections[ASection].Elements, n-1);
+end;
+
+{ Identify the ambiguous "m" token ("month" or "minute") }
+procedure TsNumFormatParser.FixMonthMinuteToken(ASection: Integer);
var
i, j: Integer;
@@ -419,7 +460,6 @@ var
end;
begin
- // Fix the ambiguous "m":
for i:=0 to High(FSections[ASection].Elements) do
// Find index of nftMonthMinute token...
if FSections[ASection].Elements[i].Token = nftMonthMinute then begin
@@ -453,41 +493,6 @@ begin
end;
end;
end;
-
- EvalNumFormatOfSection(ASection);
- {
- FSections[ASection].NumFormat,
- FSections[ASection].Decimals,
- FSections[ASection].Numerator,
- FSections[ASection].Denominator,
- FSections[ASection].CurrencySymbol,
- FSections[ASection].Color
- );
- }
-end;
-
-procedure TsNumFormatParser.ClearAll;
-var
- i, j: Integer;
-begin
- for i:=0 to Length(FSections)-1 do begin
- for j:=0 to Length(FSections[i].Elements) do
- if FSections[i].Elements <> nil then
- FSections[i].Elements[j].TextValue := '';
- FSections[i].Elements := nil;
- FSections[i].CurrencySymbol := '';
- end;
- FSections := nil;
-end;
-
-procedure TsNumFormatParser.DeleteElement(ASection, AIndex: Integer);
-var
- i, n: Integer;
-begin
- n := Length(FSections[ASection].Elements);
- for i:= AIndex+1 to n-1 do
- FSections[ASection].Elements[i-1] := FSections[ASection].Elements[i];
- SetLength(FSections[ASection].Elements, n-1);
end;
procedure TsNumFormatParser.InsertElement(ASection, AIndex: Integer;
@@ -542,22 +547,17 @@ begin
end;
procedure TsNumFormatParser.EvalNumFormatOfSection(ASection: Integer);
-{
- out ANumFormat: TsNumberFormat; out ADecimals: byte;
- out ANumerator, ADenominator: Integer; out ACurrencySymbol: String;
- out AColor: TsColor);
- }
var
nf, nf1: TsNumberFormat;
next: Integer = 0;
- decs, num, denom: Byte;
+ decs: Byte;
+ intPart, numPart, denomPart: Integer;
cs: String;
clr: TsColor;
+ tok: TsNumFormatToken;
begin
nf := nfCustom;
decs := 0;
- num := 0;
- denom := 0;
cs := '';
clr := scNotDefined;
@@ -567,7 +567,7 @@ begin
exit;
end;
- // Look for number formats
+ // Look for number formats (note: fractions are rarely used --> at end)
if IsNumberAt(ASection, 0, nf, decs, next) then begin
// nfFixed, nfFixedTh
if next = Length(Elements) then
@@ -583,28 +583,6 @@ begin
FSections[ASection].Decimals := decs;
exit;
end;
- // nfFraction
- if (IsTokenAt(nftSpace, ASection, next) or IsTextAt(' ', ASection, next)) and
- IsNumberAt(ASection, next+1, nf, num, next) and
- IsTokenAt(nftFraction, ASection, next) and
- IsNumberAt(ASection, next+1, nf, denom, next) and
- (next = Length(Elements))
- then begin
- FSections[ASection].NumFormat := nfFraction;
- FSections[ASection].FracInt := integer(decs); // "decs" means "number of integer digits", here
- FSections[ASection].FracNumerator := integer(num);
- FSections[ASection].FracDenominator := integer(denom);
- exit;
- end;
- if IsTokenAt(nftFraction, ASection, next) and
- IsNumberAt(ASection, next+1, nf, denom, next) and
- (next = Length(Elements))
- then begin
- FSections[ASection].NumFormat := nfFraction;
- FSections[ASection].FracNumerator := integer(decs);
- FSections[ASection].FracDenominator := integer(denom);
- exit;
- end;
// nfExp
if IsTokenAt(nftExpChar, ASection, next) then begin
if IsTokenAt(nftExpSign, ASection, next+1) and IsTokenAt(nftExpDigits, ASection, next+2) and
@@ -653,6 +631,17 @@ begin
FSections[ASection].NumFormat := nf;
exit;
end;
+
+ // Look for fractions
+ if IsFractionAt(ASection, 0, intPart, numPart, denomPart, next) then
+ if next = Length(Elements) then
+ begin
+ FSections[ASection].NumFormat := nfFraction;
+ FSections[ASection].FracInt := intPart;
+ FSections[ASection].FracNumerator := numPart;
+ FSections[ASection].FracDenominator := denomPart;
+ exit;
+ end;
end;
// If we get here it must be a custom format.
@@ -789,6 +778,15 @@ begin
Result := FSections[AIndex];
end;
+function TsNumFormatParser.GetTokenIntValueAt(AToken: TsNumFormatToken;
+ ASection, AIndex: Integer): Integer;
+begin
+ if IsTokenAt(AToken, ASection, AIndex) then
+ Result := FSections[ASection].Elements[AIndex].IntValue
+ else
+ Result := -1;
+end;
+
{ Checks if a currency-type of format string begins at index AIndex, and returns
the numberformat code, the count of decimals, the currency sambol, and the
color.
@@ -969,10 +967,70 @@ begin
Result := false;
end;
+function TsNumFormatParser.IsFractionAt(ASection,AIndex: Integer;
+ out AIntPart, ANumPart, ADenomPart, ANextIndex: Integer): Boolean;
+var
+ tok: TsNumFormatToken;
+ section: TsNumFormatSection;
+begin
+ Result := false;
+ AIntPart := 0;
+ ANumPart := 0;
+ ADenomPart := 0;
+ ANextIndex := MaxInt;
+
+ if ASection > High(FSections) then
+ exit;
+ section := FSections[ASection];
+ if AIndex > High(section.Elements) then
+ exit;
+
+ // integer part of the fraction
+ tok := section.Elements[AIndex].Token;
+ if tok in [nftFracIntDigit, nftFracIntSpaceDigit, nftFracIntZeroDigit] then
+ begin
+ AIntPart := section.Elements[AIndex].IntValue;
+ inc(AIndex);
+ end;
+
+ // Skip space(s)
+ while (AIndex <= High(section.Elements)) and
+ (IsTokenAt(nftSpace, ASection, AIndex) or IsTextAt(' ', ASection, AIndex))
+ do
+ inc(AIndex);
+ if AIndex > High(section.Elements) then
+ exit;
+
+ // numerator
+ tok := section.Elements[AIndex].Token;
+ if tok in [nftFracNumDigit, nftFracNumSpaceDigit, nftFracNumZeroDigit] then
+ ANumPart := section.Elements[AIndex].IntValue
+ else
+ exit;
+
+ // Skip space(s) and fraction symbol
+ inc(AIndex);
+ while (AIndex <= High(section.Elements)) and
+ (IsTokenAt(nftSpace, ASection, AIndex) or
+ IsTextAt(' ', ASection, AIndex) or
+ IsTokenAt(nftFracSymbol, ASection, AIndex))
+ do
+ inc(AIndex);
+
+ // denominator
+ tok := section.Elements[AIndex].Token;
+ if tok in [nftFracDenomDigit, nftFracDenomSpaceDigit, nftFracDenomZeroDigit] then
+ begin
+ ADenomPart := section.Elements[AIndex].IntValue;
+ ANextIndex := AIndex + 1;
+ Result := true;
+ end;
+end;
+
{ Checks whether the format tokens beginning at AIndex for ASection represent
at standard number format, like nfFixed, nfPercentage etc.
Returns TRUE if it does.
- NOTE: ADecimals can have various meanings -- see EvalNumFormatOfSection}
+ NOTE: Fraction format is not checked here --> see: IsFractionAt }
function TsNumFormatParser.IsNumberAt(ASection,AIndex: Integer;
out ANumberFormat: TsNumberFormat; out ADecimals: Byte;
out ANextIndex: Integer): Boolean;
@@ -1003,56 +1061,28 @@ begin
end;
end else
// Now look also for optional digits ('#')
- if IsTokenAt(nftOptDigit, ASection, AIndex) then begin // '#'
- if IsTokenAt(nftThSep, ASection, AIndex+1) and // ','
- IsTokenAt(nftOptDigit, ASection, AIndex+2) and // '#'
- IsTokenAt(nftOptDigit, ASection, Aindex+3) and // '#'
- IsTokenAt(nftDigit, ASection, AIndex+4) // '0'
+ if IsTokenAt(nftOptDigit, ASection, AIndex) then begin // '#'
+ if IsTokenAt(nftThSep, ASection, AIndex+1) and // ','
+ (GetTokenIntValueAt(nftOptDigit, ASection, AIndex+2) = 2) and // '##'
+ (GetTokenIntValueAt(nftDigit, ASection, AIndex+3) = 1) // '0'
then begin
- if IsTokenAt(nftDecSep, ASection, AIndex+5) and // '.'
- IsTokenAt(nftDecs, ASection, AIndex+6) // count of decimals
+ if IsTokenAt(nftDecSep, ASection, AIndex+4) and // '.'
+ IsTokenAt(nftDecs, ASection, AIndex+5) // count of decimals
then begin
// This is the case with decimal separator, like "#,##0.000"
Result := true;
ANumberFormat := nfFixedTh;
- ADecimals := FSections[ASection].Elements[AIndex+6].IntValue;
- ANextIndex := AIndex+7;
+ ADecimals := FSections[ASection].Elements[AIndex+5].IntValue;
+ ANextIndex := AIndex + 6;
end else
- if not IsTokenAt(nftDecSep, ASection, AIndex+5) then begin
+ if not IsTokenAt(nftDecSep, ASection, AIndex+4) then begin
// and this is without decimal separator, "#,##0"
result := true;
ANumberFormat := nfFixedTh;
ADecimals := 0;
- ANextIndex := AIndex + 5;
+ ANextIndex := AIndex + 4;
end;
- end else
- begin // Isolated '#'
- result := true;
- inc(AIndex);
- ANextIndex := AIndex;
- ADecimals := 1;
- while IsTokenAt(nftOptDigit, ASection, AIndex) do
- begin
- inc(AIndex);
- inc(ANextIndex);
- inc(ADecimals);
- end;
- ANumberFormat := nfFraction;
end;
- end else
- if IsTokenAt(nftOptSpaceDigit, ASection, AIndex) then // '?'
- begin
- Result := true;
- inc(AIndex);
- ANextIndex := AIndex;
- ADecimals := 1;
- while IsTokenAt(nftOptSpaceDigit, ASection, AIndex) do
- begin
- inc(AIndex);
- inc(ANextIndex);
- inc(ADecimals);
- end;
- ANumberFormat := nfFraction;
end;
end;
@@ -1602,9 +1632,12 @@ end;
procedure TsNumFormatParser.ScanNumber;
var
hasDecSep: Boolean;
+ isFrac: Boolean;
n: Integer;
+ elem: Integer;
begin
hasDecSep := false;
+ isFrac := false;
while (FCurrent < FEnd) and (FStatus = psOK) do begin
case FToken of
',': AddElement(nftThSep, ',');
@@ -1612,12 +1645,17 @@ begin
AddElement(nftDecSep, '.');
hasDecSep := true;
end;
- '0': if hasDecSep then begin
+ '0': begin
ScanAndCount('0', n);
FToken := PrevToken;
- AddElement(nftDecs, n);
- end else
- AddElement(nftDigit, '0');
+ if hasDecSep then
+ AddElement(nftDecs, n)
+ else
+ if isFrac then
+ AddElement(nftFracDenomZeroDigit, n)
+ else
+ Addelement(nftDigit, n);
+ end;
'E', 'e':
begin
AddElement(nftExpChar, FToken);
@@ -1633,10 +1671,52 @@ begin
end;
'+', '-':
AddElement(nftSign, FToken);
- '#': AddElement(nftOptDigit, FToken);
- '?': AddElement(nftOptSpaceDigit, FToken);
+ '#': begin
+ ScanAndCount('#', n);
+ FToken := PrevToken;
+ if isFrac then
+ AddElement(nftFracDenomDigit, n)
+ else
+ AddElement(nftOptDigit, n);
+ end;
+ '?': begin
+ ScanAndCount('?', n);
+ FToken := PrevToken;
+ if isFrac then
+ AddElement(nftFracDenomSpaceDigit, n)
+ else
+ AddElement(nftOptSpaceDigit, n);
+ end;
'%': AddElement(nftPercent, FToken);
- '/': AddElement(nftFraction, FToken);
+ '/': begin
+ isFrac := true;
+ AddElement(nftFracSymbol, FToken);
+ // go back and replace correct token for numerator (n=0) and integer part (n=1)
+ n := 0;
+ elem := High(FSections[FCurrSection].Elements);
+ while elem > 0 do begin
+ dec(elem);
+ case FSections[FCurrSection].Elements[elem].Token of
+ nftOptDigit:
+ if n = 0 then
+ FSections[FCurrSection].Elements[elem].Token := nftFracNumDigit
+ else
+ FSections[FCurrSection].Elements[elem].Token := nftFracIntDigit;
+ nftOptSpaceDigit:
+ if n = 0 then
+ FSections[FCurrSection].Elements[elem].Token := nftFracNumSpaceDigit
+ else
+ FSections[FCurrSection].Elements[elem].Token := nftFracIntSpaceDigit;
+ nftDigit:
+ if n = 0 then
+ FSections[FCurrSection].Elements[elem].Token := nftFracNumZeroDigit
+ else
+ FSections[FCurrSection].Elements[elem].Token := nftFracIntZeroDigit;
+ end;
+ inc(n);
+ end;
+ end;
+
else
FToken := PrevToken;
Exit;
diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas
index fc1535fd6..6540331b1 100755
--- a/components/fpspreadsheet/fpsopendocument.pas
+++ b/components/fpspreadsheet/fpsopendocument.pas
@@ -315,7 +315,7 @@ end;
function TsSpreadOpenDocNumFormatParser.BuildCurrencyXMLAsString(ASection: Integer): String;
var
- el: Integer;
+ el, next: Integer;
clr: TsColorValue;
nf: TsNumberFormat;
decs: byte;
@@ -354,15 +354,21 @@ begin
inc(el);
end;
nftOptDigit:
- if IsNumberAt(ASection, el, nf, decs, el) then
+ if IsNumberAt(ASection, el, nf, decs, next) then
+ begin
Result := Result +
' ';
+ el := next;
+ end;
nftDigit:
- if IsNumberAt(ASection, el, nf, decs, el) then
+ if IsNumberAt(ASection, el, nf, decs, next) then
+ begin
Result := Result +
' ';
+ el := next;
+ end;
nftRepeat:
begin
if FSections[ASection].Elements[el].TextValue = ' ' then
@@ -498,7 +504,7 @@ var
nf : TsNumberFormat;
decs: Byte;
expdig: Integer;
- next: Integer;
+ curr, next: Integer;
sGrouping: String;
sColor: String;
sStyleMap: String;
@@ -508,7 +514,7 @@ var
s: String;
isTimeOnly: Boolean;
isInterval: Boolean;
- num, denom: byte;
+ intPart, numPart, denomPart: Integer;
begin
Result := '';
@@ -545,14 +551,14 @@ begin
with FSections[ASection] do
begin
- next := 0;
+ curr := 0;
if IsTokenAt(nftColor, ASection, 0) then
begin
clr := FWorkbook.GetPaletteColor(Elements[0].IntValue);
sColor := '' + LineEnding;
- next := 1;
+ curr := 1;
end;
- if IsNumberAt(ASection, next, nf, decs, next) then
+ if IsNumberAt(ASection, curr, nf, decs, next) then
begin
if nf = nfFixedTh then
sGrouping := 'number:grouping="true" ';
@@ -572,39 +578,6 @@ begin
exit;
end;
- // nfFraction
- if IsTextAt(' ', ASection, next) and
- IsNumberAt(ASection, next+1, nf, num, next) and
- IsTokenAt(nftFraction, ASection, next) and
- IsNumberAt(ASection, next+1, nf, denom, next) and
- (next = Length(Elements))
- then begin
- Result :=
- '' +
- sColor +
- '' +
- '';
- exit;
- end;
- if IsTokenAt(nftFraction, ASection, next) and
- IsNumberAt(ASection, next+1, nf, denom, next) and
- (next = Length(Elements))
- then begin
- Result :=
- '' +
- sColor +
- '' +
- '';
- exit;
- end;
-
// nfPercentage
if IsTokenAt(nftPercent, ASection, next) and (next+1 = Length(Elements)) then
begin
@@ -647,7 +620,25 @@ begin
end;
end;
- // If the program gets here the format can only be nfSci, nfCurrency or date/time.
+ // nfFraction
+ if IsFractionAt(ASection, curr, intPart, numPart, denomPart, next)
+ then begin
+ Result :=
+ '' +
+ sColor +
+ ' 0 then
+ Result := Result +
+ 'number:min-integer-digits="' + IntToStr(intPart) + '" ';
+ Result := Result +
+ 'number:min-numerator-digits="' + IntToStr(numPart) + '" ' +
+ 'number:min-denominator-digits="' + IntToStr(denomPart) + '" ' +
+ '/>' +
+ '';
+ exit;
+ end;
+
+ // If the program gets here the format can only be Currency or date/time.
el := 0;
decs := 0;
while el < Length(Elements) do