From 2d3591316916269f908de44fd4261c50f350cab6 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 15 May 2015 17:33:24 +0000 Subject: [PATCH] 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 --- .../fpspreadsheet/fpsnumformatparser.pas | 23 ++++++++++++++++--- components/fpspreadsheet/fpsopendocument.pas | 20 ++++++++++++++++ components/fpspreadsheet/fpstypes.pas | 23 +++++++++++++++---- components/fpspreadsheet/fpsutils.pas | 2 ++ 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/components/fpspreadsheet/fpsnumformatparser.pas b/components/fpspreadsheet/fpsnumformatparser.pas index a354e5418..2e3534197 100644 --- a/components/fpspreadsheet/fpsnumformatparser.pas +++ b/components/fpspreadsheet/fpsnumformatparser.pas @@ -127,7 +127,7 @@ type implementation uses - TypInfo, LazUTF8, fpsutils, fpsCurrency; + TypInfo, Math, LazUTF8, fpsutils, fpsCurrency; function CreateNumFormatParams(AWorkbook: TsWorkbook; @@ -315,6 +315,13 @@ begin FStatus := psErrMultipleExpChars else 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: if (nfkFraction in section^.Kind) then FStatus := psErrMultipleFracSymbols @@ -941,6 +948,8 @@ end; procedure TsNumFormatParser.ScanAndCount(ATestChar: Char; out ACount: Integer); begin ACount := 0; + if FToken <> ATestChar then + exit; repeat inc(ACount); FToken := NextToken; @@ -1236,7 +1245,7 @@ procedure TsNumFormatParser.ScanNumber; var hasDecSep: Boolean; isFrac: Boolean; - n: Integer; + n, m: Integer; el: Integer; savedCurrent: PChar; begin @@ -1254,12 +1263,13 @@ begin savedCurrent := FCurrent; if not (hasDecSep or isFrac) and (n = 1) and (FToken = ',') then begin + m := 0; FToken := NextToken; ScanAndCount('#', n); case n of 0: begin - FToken := PrevToken; ScanAndCount('0', n); + ScanAndCount(',', m); FToken := prevToken; if n = 3 then AddElement(nftIntTh, 3) @@ -1268,6 +1278,7 @@ begin end; 1: begin ScanAndCount('0', n); + ScanAndCount(',', m); FToken := prevToken; if n = 2 then AddElement(nftIntTh, 2) @@ -1276,6 +1287,7 @@ begin end; 2: begin ScanAndCount('0', n); + ScanAndCount(',', m); FToken := prevToken; if (n = 1) then AddElement(nftIntTh, 1) @@ -1283,6 +1295,8 @@ begin FCurrent := savedCurrent; end; end; + if m > 0 then + AddElement(nftFactor, m); end else begin FToken := PrevToken; @@ -1297,6 +1311,7 @@ begin end; '0': begin ScanAndCount('0', n); + ScanAndCount(',', m); FToken := PrevToken; if hasDecSep then AddElement(nftZeroDecs, n) @@ -1305,6 +1320,8 @@ begin AddElement(nftFracDenomZeroDigit, n) else AddElement(nftIntZeroDigit, n); + if m > 0 then + AddElement(nftFactor, m); end; '1'..'9': begin diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index ac1ef47e6..504ecac95 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -620,6 +620,9 @@ begin nftPercent: Result := Result + '%'; + nftFactor: + ; + nftCurrSymbol: Result := Result + '' + Elements[el].TextValue + ''; @@ -635,6 +638,8 @@ begin end else if (el = nel-1) or (Elements[el+1].Token <> nftDecSep) then 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 + ' />'; end; @@ -740,6 +745,8 @@ begin Result := Result + ' number:min-integer-digits="' + IntToStr(n) + '"'; n := IfThen(Elements[el+2].Token = nftZeroDecs, Elements[el+2].IntValue, 1); 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 + ' />'; inc(el, 2); end @@ -750,6 +757,8 @@ begin Result := Result + ' 0) then + Result := Result + Format(' number:display-factor="%.0f"', [1.0/Factor]); Result := Result + ' />'; end; end; @@ -2227,6 +2236,7 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode); nfs: String; decs: Byte; s: String; + f: Double; fracInt, fracNum, fracDenom: Integer; grouping: Boolean; nex: Integer; @@ -2253,8 +2263,18 @@ procedure TsSpreadOpenDocReader.ReadNumFormats(AStylesNode: TDOMNode); if s = '' then s := GetAttrValue(node, 'decimal-places'); if s <> '' then decs := StrToInt(s) else decs := 0; 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); 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 if nodeName = 'number:fraction' then begin diff --git a/components/fpspreadsheet/fpstypes.pas b/components/fpspreadsheet/fpstypes.pas index 051c91aab..6f8798f06 100644 --- a/components/fpspreadsheet/fpstypes.pas +++ b/components/fpspreadsheet/fpstypes.pas @@ -473,6 +473,7 @@ type nftExpSign, // '+' or '-' in exponent nftExpDigits, // '0' digits in exponent, count stored in IntValue nftPercent, // '%' percent symbol + nftFactor, // thousand separators at end of format string, each one divides value by 1000 nftFracSymbol, // '/' fraction symbol nftFracNumOptDigit, // '#' in numerator, count stored in IntValue nftFracNumSpaceDigit, // '?' in numerator, count stored in IntValue @@ -502,7 +503,7 @@ type TsNumFormatElements = array of TsNumFormatElement; TsNumFormatKind = (nfkPercent, nfkExp, nfkCurrency, nfkFraction, - nfkDate, nfkTime, nfkTimeInterval, nfkHasColor, nfkHasThSep); + nfkDate, nfkTime, nfkTimeInterval, nfkHasColor, nfkHasThSep, nfkHasFactor); TsNumFormatKinds = set of TsNumFormatKind; TsNumFormatSection = record @@ -510,6 +511,7 @@ type Kind: TsNumFormatKinds; NumFormat: TsNumberFormat; Decimals: Byte; + Factor: Double; FracInt: Integer; FracNumerator: Integer; FracDenominator: Integer; @@ -721,7 +723,6 @@ const HEADER_FOOTER_INDEX_FIRST = 0; HEADER_FOOTER_INDEX_ODD = 1; HEADER_FOOTER_INDEX_EVEN = 2; - HEADER_FOOTER_INDEX_ODDEVEN = 1; HEADER_FOOTER_INDEX_ALL = 1; function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; @@ -907,7 +908,7 @@ end; function BuildFormatStringFromSection(const ASection: TsNumFormatSection): String; var element: TsNumFormatElement; - i: Integer; + i, n: Integer; begin Result := ''; @@ -940,6 +941,16 @@ begin Result := Result + '/'; nftPercent: Result := Result + '%'; + nftFactor: + if element.IntValue <> 0 then + begin + n := element.IntValue; + while (n > 0) do + begin + Result := Result + ','; + dec(n); + end; + end; nftSpace: Result := Result + ' '; nftText: @@ -1075,6 +1086,10 @@ begin exit; if Sections[i].Decimals <> ASections[i].Decimals then exit; + { + if Sections[i].Factor <> ASections[i].Factor then + exit; + } if Sections[i].FracInt <> ASections[i].FracInt then exit; if Sections[i].FracNumerator <> ASections[i].FracNumerator then @@ -1099,7 +1114,7 @@ begin nftHour, nftMinute, nftSecond, nftMilliseconds, nftMonthMinute, nftIntOptDigit, nftIntZeroDigit, nftIntSpaceDigit, nftIntTh, - nftZeroDecs, nftOptDecs, nftSpaceDecs, nftExpDigits, + nftZeroDecs, nftOptDecs, nftSpaceDecs, nftExpDigits, nftFactor, nftFracNumOptDigit, nftFracNumSpaceDigit, nftFracNumZeroDigit, nftFracDenomOptDigit, nftFracDenomSpaceDigit, nftFracDenomZeroDigit, nftColor: diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 0064f67ab..f0e4c8886 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -2967,6 +2967,8 @@ begin if nfkPercent in section.Kind then AValue := AValue * 100.0; + if nfkHasFactor in section.Kind then + AValue := AValue * section.Factor; if nfkTime in section.Kind then DecodeTime(AValue, hr, min, sec, ms); if nfkDate in section.Kind then