fpspreadsheet: Add support for number formats splitting off thousands, (Excel syntax "0.0,"), millions ("0.0,,"), etc.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4130 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-05-15 17:33:24 +00:00
parent 6408cfff3a
commit 2d35913169
4 changed files with 61 additions and 7 deletions

View File

@@ -127,7 +127,7 @@ type
implementation implementation
uses uses
TypInfo, LazUTF8, fpsutils, fpsCurrency; TypInfo, Math, LazUTF8, fpsutils, fpsCurrency;
function CreateNumFormatParams(AWorkbook: TsWorkbook; function CreateNumFormatParams(AWorkbook: TsWorkbook;
@@ -315,6 +315,13 @@ begin
FStatus := psErrMultipleExpChars FStatus := psErrMultipleExpChars
else else
section^.Kind := section^.Kind + [nfkExp]; section^.Kind := section^.Kind + [nfkExp];
nftFactor:
if section^.Elements[el].IntValue <> 0 then
begin
section^.Elements[el].FloatValue := IntPower(10, -3*section^.Elements[el].IntValue);
section^.Factor := section^.Elements[el].FloatValue;
section^.Kind := section^.Kind + [nfkHasFactor];
end;
nftFracSymbol: nftFracSymbol:
if (nfkFraction in section^.Kind) then if (nfkFraction in section^.Kind) then
FStatus := psErrMultipleFracSymbols FStatus := psErrMultipleFracSymbols
@@ -941,6 +948,8 @@ end;
procedure TsNumFormatParser.ScanAndCount(ATestChar: Char; out ACount: Integer); procedure TsNumFormatParser.ScanAndCount(ATestChar: Char; out ACount: Integer);
begin begin
ACount := 0; ACount := 0;
if FToken <> ATestChar then
exit;
repeat repeat
inc(ACount); inc(ACount);
FToken := NextToken; FToken := NextToken;
@@ -1236,7 +1245,7 @@ procedure TsNumFormatParser.ScanNumber;
var var
hasDecSep: Boolean; hasDecSep: Boolean;
isFrac: Boolean; isFrac: Boolean;
n: Integer; n, m: Integer;
el: Integer; el: Integer;
savedCurrent: PChar; savedCurrent: PChar;
begin begin
@@ -1254,12 +1263,13 @@ begin
savedCurrent := FCurrent; savedCurrent := FCurrent;
if not (hasDecSep or isFrac) and (n = 1) and (FToken = ',') then if not (hasDecSep or isFrac) and (n = 1) and (FToken = ',') then
begin begin
m := 0;
FToken := NextToken; FToken := NextToken;
ScanAndCount('#', n); ScanAndCount('#', n);
case n of case n of
0: begin 0: begin
FToken := PrevToken;
ScanAndCount('0', n); ScanAndCount('0', n);
ScanAndCount(',', m);
FToken := prevToken; FToken := prevToken;
if n = 3 then if n = 3 then
AddElement(nftIntTh, 3) AddElement(nftIntTh, 3)
@@ -1268,6 +1278,7 @@ begin
end; end;
1: begin 1: begin
ScanAndCount('0', n); ScanAndCount('0', n);
ScanAndCount(',', m);
FToken := prevToken; FToken := prevToken;
if n = 2 then if n = 2 then
AddElement(nftIntTh, 2) AddElement(nftIntTh, 2)
@@ -1276,6 +1287,7 @@ begin
end; end;
2: begin 2: begin
ScanAndCount('0', n); ScanAndCount('0', n);
ScanAndCount(',', m);
FToken := prevToken; FToken := prevToken;
if (n = 1) then if (n = 1) then
AddElement(nftIntTh, 1) AddElement(nftIntTh, 1)
@@ -1283,6 +1295,8 @@ begin
FCurrent := savedCurrent; FCurrent := savedCurrent;
end; end;
end; end;
if m > 0 then
AddElement(nftFactor, m);
end else end else
begin begin
FToken := PrevToken; FToken := PrevToken;
@@ -1297,6 +1311,7 @@ begin
end; end;
'0': begin '0': begin
ScanAndCount('0', n); ScanAndCount('0', n);
ScanAndCount(',', m);
FToken := PrevToken; FToken := PrevToken;
if hasDecSep then if hasDecSep then
AddElement(nftZeroDecs, n) AddElement(nftZeroDecs, n)
@@ -1305,6 +1320,8 @@ begin
AddElement(nftFracDenomZeroDigit, n) AddElement(nftFracDenomZeroDigit, n)
else else
AddElement(nftIntZeroDigit, n); AddElement(nftIntZeroDigit, n);
if m > 0 then
AddElement(nftFactor, m);
end; end;
'1'..'9': '1'..'9':
begin begin

View File

@@ -620,6 +620,9 @@ begin
nftPercent: nftPercent:
Result := Result + '<number:text>%</number:text>'; Result := Result + '<number:text>%</number:text>';
nftFactor:
;
nftCurrSymbol: nftCurrSymbol:
Result := Result + '<number:currency-symbol>' + Elements[el].TextValue + '</number:currency-symbol>'; Result := Result + '<number:currency-symbol>' + Elements[el].TextValue + '</number:currency-symbol>';
@@ -635,6 +638,8 @@ begin
end else end else
if (el = nel-1) or (Elements[el+1].Token <> nftDecSep) then if (el = nel-1) or (Elements[el+1].Token <> nftDecSep) then
Result := Result + ' number:decimal-places="0"'; Result := Result + ' number:decimal-places="0"';
if (nfkHasFactor in Kind) and (Factor <> 0) then
Result := Result + Format(' number:display-factor="%.0f"', [1.0/Factor]);
Result := Result + ' />'; Result := Result + ' />';
end; end;
@@ -740,6 +745,8 @@ begin
Result := Result + ' number:min-integer-digits="' + IntToStr(n) + '"'; Result := Result + ' number:min-integer-digits="' + IntToStr(n) + '"';
n := IfThen(Elements[el+2].Token = nftZeroDecs, Elements[el+2].IntValue, 1); n := IfThen(Elements[el+2].Token = nftZeroDecs, Elements[el+2].IntValue, 1);
Result := Result + ' number:decimal-places="' + IntToStr(n) + '"'; Result := Result + ' number:decimal-places="' + IntToStr(n) + '"';
if (nfkHasFactor in Kind) and (Factor <> 0) then
Result := Result + Format(' number:display-factor="%.0f"', [1.0/Factor]);
Result := Result + ' />'; Result := Result + ' />';
inc(el, 2); inc(el, 2);
end end
@@ -750,6 +757,8 @@ begin
Result := Result + '<number:number number:decimal-places="0"'; Result := Result + '<number:number number:decimal-places="0"';
n := IfThen(Elements[el].Token = nftIntZeroDigit, Elements[el].IntValue, 1); n := IfThen(Elements[el].Token = nftIntZeroDigit, Elements[el].IntValue, 1);
Result := Result + ' number:min-integer-digits="' + IntToStr(n) + '"'; Result := Result + ' number:min-integer-digits="' + IntToStr(n) + '"';
if (nfkHasFactor in Kind) and (Factor <> 0) then
Result := Result + Format(' number:display-factor="%.0f"', [1.0/Factor]);
Result := Result + ' />'; Result := Result + ' />';
end; end;
end; end;
@@ -2227,6 +2236,7 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
nfs: String; nfs: String;
decs: Byte; decs: Byte;
s: String; s: String;
f: Double;
fracInt, fracNum, fracDenom: Integer; fracInt, fracNum, fracDenom: Integer;
grouping: Boolean; grouping: Boolean;
nex: Integer; nex: Integer;
@@ -2253,8 +2263,18 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode);
if s = '' then s := GetAttrValue(node, 'decimal-places'); if s = '' then s := GetAttrValue(node, 'decimal-places');
if s <> '' then decs := StrToInt(s) else decs := 0; if s <> '' then decs := StrToInt(s) else decs := 0;
grouping := GetAttrValue(node, 'number:grouping') = 'true'; grouping := GetAttrValue(node, 'number:grouping') = 'true';
s := GetAttrValue(node, 'number:display-factor');
if s <> '' then f := StrToFloat(s, FPointSeparatorSettings) else f := 1.0;
nf := IfThen(grouping, nfFixedTh, nfFixed); nf := IfThen(grouping, nfFixedTh, nfFixed);
nfs := nfs + BuildNumberFormatString(nf, Workbook.FormatSettings, decs); nfs := nfs + BuildNumberFormatString(nf, Workbook.FormatSettings, decs);
if f <> 1.0 then begin
nf := nfCustom;
while (f > 1.0) do
begin
nfs := nfs + ',';
f := f / 1000;
end;
end;
end else end else
if nodeName = 'number:fraction' then if nodeName = 'number:fraction' then
begin begin

View File

@@ -473,6 +473,7 @@ type
nftExpSign, // '+' or '-' in exponent nftExpSign, // '+' or '-' in exponent
nftExpDigits, // '0' digits in exponent, count stored in IntValue nftExpDigits, // '0' digits in exponent, count stored in IntValue
nftPercent, // '%' percent symbol nftPercent, // '%' percent symbol
nftFactor, // thousand separators at end of format string, each one divides value by 1000
nftFracSymbol, // '/' fraction symbol nftFracSymbol, // '/' fraction symbol
nftFracNumOptDigit, // '#' in numerator, count stored in IntValue nftFracNumOptDigit, // '#' in numerator, count stored in IntValue
nftFracNumSpaceDigit, // '?' in numerator, count stored in IntValue nftFracNumSpaceDigit, // '?' in numerator, count stored in IntValue
@@ -502,7 +503,7 @@ type
TsNumFormatElements = array of TsNumFormatElement; TsNumFormatElements = array of TsNumFormatElement;
TsNumFormatKind = (nfkPercent, nfkExp, nfkCurrency, nfkFraction, TsNumFormatKind = (nfkPercent, nfkExp, nfkCurrency, nfkFraction,
nfkDate, nfkTime, nfkTimeInterval, nfkHasColor, nfkHasThSep); nfkDate, nfkTime, nfkTimeInterval, nfkHasColor, nfkHasThSep, nfkHasFactor);
TsNumFormatKinds = set of TsNumFormatKind; TsNumFormatKinds = set of TsNumFormatKind;
TsNumFormatSection = record TsNumFormatSection = record
@@ -510,6 +511,7 @@ type
Kind: TsNumFormatKinds; Kind: TsNumFormatKinds;
NumFormat: TsNumberFormat; NumFormat: TsNumberFormat;
Decimals: Byte; Decimals: Byte;
Factor: Double;
FracInt: Integer; FracInt: Integer;
FracNumerator: Integer; FracNumerator: Integer;
FracDenominator: Integer; FracDenominator: Integer;
@@ -721,7 +723,6 @@ const
HEADER_FOOTER_INDEX_FIRST = 0; HEADER_FOOTER_INDEX_FIRST = 0;
HEADER_FOOTER_INDEX_ODD = 1; HEADER_FOOTER_INDEX_ODD = 1;
HEADER_FOOTER_INDEX_EVEN = 2; HEADER_FOOTER_INDEX_EVEN = 2;
HEADER_FOOTER_INDEX_ODDEVEN = 1;
HEADER_FOOTER_INDEX_ALL = 1; HEADER_FOOTER_INDEX_ALL = 1;
function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String;
@@ -907,7 +908,7 @@ end;
function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String;
var var
element: TsNumFormatElement; element: TsNumFormatElement;
i: Integer; i, n: Integer;
begin begin
Result := ''; Result := '';
@@ -940,6 +941,16 @@ begin
Result := Result + '/'; Result := Result + '/';
nftPercent: nftPercent:
Result := Result + '%'; Result := Result + '%';
nftFactor:
if element.IntValue <> 0 then
begin
n := element.IntValue;
while (n > 0) do
begin
Result := Result + ',';
dec(n);
end;
end;
nftSpace: nftSpace:
Result := Result + ' '; Result := Result + ' ';
nftText: nftText:
@@ -1075,6 +1086,10 @@ begin
exit; exit;
if Sections[i].Decimals <> ASections[i].Decimals then if Sections[i].Decimals <> ASections[i].Decimals then
exit; exit;
{
if Sections[i].Factor <> ASections[i].Factor then
exit;
}
if Sections[i].FracInt <> ASections[i].FracInt then if Sections[i].FracInt <> ASections[i].FracInt then
exit; exit;
if Sections[i].FracNumerator <> ASections[i].FracNumerator then if Sections[i].FracNumerator <> ASections[i].FracNumerator then
@@ -1099,7 +1114,7 @@ begin
nftHour, nftMinute, nftSecond, nftMilliseconds, nftHour, nftMinute, nftSecond, nftMilliseconds,
nftMonthMinute, nftMonthMinute,
nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit, nftIntTh, nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit, nftIntTh,
nftZeroDecs, nftOptDecs, nftSpaceDecs, nftExpDigits, nftZeroDecs, nftOptDecs, nftSpaceDecs, nftExpDigits, nftFactor,
nftFracNumOptDigit, nftFracNumSpaceDigit, nftFracNumZeroDigit, nftFracNumOptDigit, nftFracNumSpaceDigit, nftFracNumZeroDigit,
nftFracDenomOptDigit, nftFracDenomSpaceDigit, nftFracDenomZeroDigit, nftFracDenomOptDigit, nftFracDenomSpaceDigit, nftFracDenomZeroDigit,
nftColor: nftColor:

View File

@@ -2967,6 +2967,8 @@ begin
if nfkPercent in section.Kind then if nfkPercent in section.Kind then
AValue := AValue * 100.0; AValue := AValue * 100.0;
if nfkHasFactor in section.Kind then
AValue := AValue * section.Factor;
if nfkTime in section.Kind then if nfkTime in section.Kind then
DecodeTime(AValue, hr, min, sec, ms); DecodeTime(AValue, hr, min, sec, ms);
if nfkDate in section.Kind then if nfkDate in section.Kind then