From 03ccd5323a38981aa74c2a52951ae4753d855c13 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 16 Jun 2014 14:38:17 +0000 Subject: [PATCH] fpspreadsheet: Infrastructure for writing of number formats to ods files. Fixed and exponential formats ok, percentage not working. Others not yet implemented. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3174 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/opendocdemo/opendocwrite.lpr | 145 ++++++++++++++++++ components/fpspreadsheet/fpsopendocument.pas | 139 +++++++++++------ 2 files changed, 236 insertions(+), 48 deletions(-) diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr index 7bd30f1ae..38f8bf152 100644 --- a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr +++ b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr @@ -17,8 +17,19 @@ var MyWorkbook: TsWorkbook; MyWorksheet: TsWorksheet; MyDir: string; + number1, number2, number3, number4, + number5, number6, number7, number8: Double; + row: Integer; begin MyDir := ExtractFilePath(ParamStr(0)); + number1 := 1.23456789; + number2 := -number1; + number3 := 0.123456789; + number4 := -number3; + number5 := 10000*number1; + number6 := -10000*number1; + number7 := 1/number3; + number8 := -1/number3; // Create the spreadsheet MyWorkbook := TsWorkbook.Create; @@ -37,6 +48,140 @@ begin MyWorksheet.WriteUsedFormatting(0, 0, [uffBold]); MyWorksheet.WriteFont(0, 1, 'Times New Roman', 16, [], scRed); + // Show number formats + row := 7; + MyWorksheet.WriteUTF8Text(row, 0, 'Number formats:'); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfGeneral'); + MyWorksheet.WriteNumber(row, 1, number1, nfGeneral); + MyWorksheet.WriteNumber(row, 2, number2, nfGeneral); + MyWorksheet.WriteNumber(row, 3, number3, nfGeneral); + MyWorksheet.WriteNumber(row, 4, number4, nfGeneral); + MyWorksheet.WriteNumber(row, 5, number5, nfGeneral); + MyWorksheet.WriteNumber(row, 6, number6, nfGeneral); + MyWorksheet.WriteNumber(row, 7, number7, nfGeneral); + MyWorksheet.WriteNumber(row, 8, number8, nfGeneral); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixed, 0 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixed, 0); + MyWorksheet.WriteNumber(row, 2, number2, nfFixed, 0); + MyWorksheet.WriteNumber(row, 3, number3, nfFixed, 0); + MyWorksheet.WriteNumber(row, 4, number4, nfFixed, 0); + MyWorksheet.WriteNumber(row, 5, number5, nfFixed, 0); + MyWorksheet.WriteNumber(row, 6, number6, nfFixed, 0); + MyWorksheet.WriteNumber(row, 7, number7, nfFixed, 0); + MyWorksheet.WriteNumber(row, 8, number8, nfFixed, 0); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixed, 2 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixed, 2); + MyWorksheet.WriteNumber(row, 2, number2, nfFixed, 2); + MyWorksheet.WriteNumber(row, 3, number3, nfFixed, 2); + MyWorksheet.WriteNumber(row, 4, number4, nfFixed, 2); + MyWorksheet.WriteNumber(row, 5, number5, nfFixed, 2); + MyWorksheet.WriteNumber(row, 6, number6, nfFixed, 2); + MyWorksheet.WriteNumber(row, 7, number7, nfFixed, 2); + MyWorksheet.WriteNumber(row, 8, number8, nfFixed, 2); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixed, 3 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixed, 3); + MyWorksheet.WriteNumber(row, 2, number2, nfFixed, 3); + MyWorksheet.WriteNumber(row, 3, number3, nfFixed, 3); + MyWorksheet.WriteNumber(row, 4, number4, nfFixed, 3); + MyWorksheet.WriteNumber(row, 5, number5, nfFixed, 3); + MyWorksheet.WriteNumber(row, 6, number6, nfFixed, 3); + MyWorksheet.WriteNumber(row, 7, number7, nfFixed, 3); + MyWorksheet.WriteNumber(row, 8, number8, nfFixed, 3); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixedTh, 0 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 2, number2, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 3, number3, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 4, number4, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 5, number5, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 6, number6, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 7, number7, nfFixedTh, 0); + MyWorksheet.WriteNumber(row, 8, number8, nfFixedTh, 0); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixedTh, 2 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 2, number2, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 3, number3, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 4, number4, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 5, number5, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 6, number6, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 7, number7, nfFixedTh, 2); + MyWorksheet.WriteNumber(row, 8, number8, nfFixedTh, 2); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfFixedTh, 3 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 2, number2, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 3, number3, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 4, number4, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 5, number5, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 6, number6, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 7, number7, nfFixedTh, 3); + MyWorksheet.WriteNumber(row, 8, number8, nfFixedTh, 3); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfPercentage, 0 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 2, number2, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 3, number3, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 4, number4, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 5, number5, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 6, number6, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 7, number7, nfPercentage, 0); + MyWorksheet.WriteNumber(row, 8, number8, nfPercentage, 0); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfPercentage, 2 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 2, number2, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 3, number3, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 4, number4, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 5, number5, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 6, number6, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 7, number7, nfPercentage, 2); + MyWorksheet.WriteNumber(row, 8, number8, nfPercentage, 2); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfPercentage, 3 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 2, number2, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 3, number3, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 4, number4, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 5, number5, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 6, number6, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 7, number7, nfPercentage, 3); + MyWorksheet.WriteNumber(row, 8, number8, nfPercentage, 3); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfExp, 0 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfExp, 0); + MyWorksheet.WriteNumber(row, 2, number2, nfExp, 0); + MyWorksheet.WriteNumber(row, 3, number3, nfExp, 0); + MyWorksheet.WriteNumber(row, 4, number4, nfExp, 0); + MyWorksheet.WriteNumber(row, 5, number5, nfExp, 0); + MyWorksheet.WriteNumber(row, 6, number6, nfExp, 0); + MyWorksheet.WriteNumber(row, 7, number7, nfExp, 0); + MyWorksheet.WriteNumber(row, 8, number8, nfExp, 0); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfExp, 2 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfExp, 2); + MyWorksheet.WriteNumber(row, 2, number2, nfExp, 2); + MyWorksheet.WriteNumber(row, 3, number3, nfExp, 2); + MyWorksheet.WriteNumber(row, 4, number4, nfExp, 2); + MyWorksheet.WriteNumber(row, 5, number5, nfExp, 2); + MyWorksheet.WriteNumber(row, 6, number6, nfExp, 2); + MyWorksheet.WriteNumber(row, 7, number7, nfExp, 2); + MyWorksheet.WriteNumber(row, 8, number8, nfExp, 2); + inc(row); + MyWorksheet.WriteUTF8Text(row, 0, 'nfExp, 3 decimals'); + MyWorksheet.WriteNumber(row, 1, number1, nfExp, 3); + MyWorksheet.WriteNumber(row, 2, number2, nfExp, 3); + MyWorksheet.WriteNumber(row, 3, number3, nfExp, 3); + MyWorksheet.WriteNumber(row, 4, number4, nfExp, 3); + MyWorksheet.WriteNumber(row, 5, number5, nfExp, 3); + MyWorksheet.WriteNumber(row, 6, number6, nfExp, 3); + MyWorksheet.WriteNumber(row, 7, number7, nfExp, 3); + MyWorksheet.WriteNumber(row, 8, number8, nfExp, 3); + // Creates a new worksheet MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet 2'); diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 877dc8c0d..c1aa080d8 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -59,10 +59,10 @@ type { TsSpreadOpenDocNumFormatParser } TsSpreadOpenDocNumFormatParser = class(TsNumFormatParser) protected - function BuildXMLAsStringFromSection(ASection: Integer; AIndent: String; - AFormatNo: Integer): String; + function BuildXMLAsStringFromSection(ASection: Integer; + AIndent,AFormatName: String): String; public - function BuildXMLAsString(AIndent: String; AFormatNo: Integer): String; + function BuildXMLAsString(AIndent,AFormatName: String): String; end; { TsSpreadOpenDocReader } @@ -154,6 +154,7 @@ type // Helpers procedure CreateNumFormatList; override; procedure ListAllColumnStyles; + procedure ListAllNumFormats; override; procedure ListAllRowStyles; // Routines to write those files procedure WriteMimetype; @@ -297,40 +298,45 @@ type procedure TsSpreadOpenDocNumFormatList.AddBuiltinFormats; begin - // there are no built-in number formats which are silently assumed to exist. + AddFormat('N0', '', nfGeneral); + AddFormat('N1', '0', nfFixed); + AddFormat('N2', '0.00', nfFixed); + AddFormat('N3', '#,##0', nfFixedTh); + AddFormat('N4', '#,##0.00', nfFixed); + AddFormat('N10', '0%', nfPercentage); + AddFormat('N11', '0.00%', nfPercentage); end; { TsSpreadOpenDocNumFormatParser } -function TsSpreadOpenDocNumFormatParser.BuildXMLAsString(AIndent: String; - AFormatNo: Integer): String; +function TsSpreadOpenDocNumFormatParser.BuildXMLAsString(AIndent, + AFormatName: String): String; var i, ns: Integer; begin Result := ''; for i := Length(FSections)-1 downto 0 do - Result := Result + BuildXMLAsStringFromSection(i, AIndent, AFormatNo); + Result := Result + BuildXMLAsStringFromSection(i, AIndent, AFormatName); end; function TsSpreadOpenDocNumFormatParser.BuildXMLAsStringFromSection( - ASection: Integer; AIndent: String; AFormatNo: Integer): String; + ASection: Integer; AIndent,AFormatName: String): String; var nf : TsNumberFormat; decs: Byte; + expdig: Integer; next: Integer; sGrouping: String; sColor: String; sStyleMap: String; ns: Integer; - fmtName: String; clr: TsColorvalue; begin Result := ''; sGrouping := ''; sColor := ''; sStyleMap := ''; - fmtName := Format('N%d', [AFormatNo]); ns := Length(FSections); if (ns > 1) then begin @@ -338,20 +344,20 @@ begin case ns of 2: sStyleMap := AIndent + ' ' + LineEnding; // >= 0 3: sStyleMap := AIndent + ' 0 + 'style:apply-style-name="' + AFormatName + 'P0" ' + // > 0 'style:condition="value()>0" />' + LineEnding + AIndent + ' ' + LineEnding; else raise Exception.Create('At most 3 format sections allowed.'); end else - fmtName := fmtName + 'P' + IntToStr(ASection); + AFormatName := AFormatName + 'P' + IntToStr(ASection); end; with FSections[ASection] do begin @@ -368,7 +374,7 @@ begin // nfFixed, nfFixedTh if (next = Length(Elements)) then begin Result := AIndent + - '' + LineEnding + + '' + LineEnding + sColor + AIndent + ' ' + LineEnding; exit; end; - end; - // nfPercentage - if IsTokenAt(nftPercent, ASection, next) and (next+1 = Length(Elements)) - then begin - Result := AIndent + - '' + LineEnding + - sColor + AIndent + - ' ' + LineEnding + AIndent + - ' %' + LineEnding + - sStyleMap + AIndent + - '' + LineEnding; - exit; + // nfPercentage + if IsTokenAt(nftPercent, ASection, next) and (next+1 = Length(Elements)) + then begin + Result := AIndent + + '' + LineEnding + + sColor + AIndent + + ' ' + LineEnding + AIndent + + ' %' + LineEnding + + sStyleMap + AIndent + + '' + LineEnding; + exit; + end; + + // nfExp + if IsTokenAt(nftExpChar, ASection, next) then begin + if (next + 2 < Length(Elements)) and + IsTokenAt(nftExpSign, ASection, next+1) and + IsTokenAt(nftExpDigits, ASection, next+2) + then + expdig := Elements[next+2].IntValue + else + if (next + 1 < Length(Elements)) and + IsTokenAt(nftExpDigits, ASection, next+1) + then + expdig := Elements[next+1].IntValue + else + exit; + Result := AIndent + + '' + LineEnding + + sColor + AIndent + + ' ' + + sStylemap + AIndent + + ''; + exit; + end; end; end; @@ -1850,6 +1880,24 @@ begin end; end; +{ Collects all number formats used in the workbook. Overrides the inherited + method to assign a unique name according to the OpenDocument syntax ("N" + to the format items. } +procedure TsSpreadOpenDocWriter.ListAllNumFormats; +const + FMT_BASE = 1000; // Format number to start with. Not clear if this is correct... +var + n, i, j: Integer; +begin + n := NumFormatList.Count; + inherited ListAllNumFormats; + j := 0; + for i:=n to NumFormatList.Count-1 do begin + NumFormatList.Items[i].Name := Format('N%d', [FMT_BASE + j]); + inc(j); + end; +end; + procedure TsSpreadOpenDocWriter.ListAllRowStyles; var i, j, r: Integer; @@ -2131,14 +2179,22 @@ function TsSpreadOpenDocWriter.WriteCellStylesXMLAsString: string; var i: Integer; s: String; + fmtIndex: Integer; + fmt: String; begin Result := ''; for i := 0 to Length(FFormattingStyles) - 1 do begin + fmtIndex := NumFormatList.Find(FFormattingStyles[i].NumberFormatStr); + if fmtIndex <> -1 + then fmt := 'style:data-style-name="' + NumFormatList[fmtIndex].Name +'"' + else fmt := ''; + // Start and Name Result := Result + - ' ' + LineEnding; + ' ' + LineEnding; // Fields @@ -2256,29 +2312,16 @@ function TsSpreadOpenDocWriter.WriteNumFormatsXMLAsString: String; var i: Integer; numFmtXML: String; + fmtItem: TsNumFormatData; parser: TsSpreadOpenDocNumFormatParser; begin -{ - - - - -' - - - -' - - -} Result := ''; - ListAllNumFormats; - for i:=0 to FNumFormatList.Count-1 do begin - parser := TsSpreadOpenDocNumFormatParser.Create(Workbook, FNumFormatList.Items[i].FormatString); + fmtItem := FNumFormatList.Items[i]; + parser := TsSpreadOpenDocNumFormatParser.Create(Workbook, fmtItem.FormatString); try - numFmtXML := parser.BuildXMLAsString(' ', 1000+i); //120+i); // Don't know where the user numbers start... + numFmtXML := parser.BuildXMLAsString(' ', fmtItem.Name); if numFmtXML <> '' then Result := Result + numFmtXML; finally