From 3f292f5e99e1770859dec18899c02b057d91dd18 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 14 Jul 2014 20:47:53 +0000 Subject: [PATCH] fpspreadsheet: Writing of number formats for xlsx (date/time not working yet) git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3318 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/fpspreadsheet.pas | 2 +- components/fpspreadsheet/xlsxooxml.pas | 148 ++++++++++++++++++--- 2 files changed, 127 insertions(+), 23 deletions(-) diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 2721c5665..4f886e322 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -5172,7 +5172,7 @@ end; ConvertBeforeWriting in order to convert the fpc format strings to the dialect used in the file. - @param AIndex The number format at this item is considered. + @param AIndex Index of the format item under consideration. @return String of formatting codes that will be written to the file. } function TsCustomNumFormatList.FormatStringForWriting(AIndex: Integer): String; var diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas index eafa8590d..5dfdcad07 100755 --- a/components/fpspreadsheet/xlsxooxml.pas +++ b/components/fpspreadsheet/xlsxooxml.pas @@ -44,15 +44,10 @@ type { TsOOXMLFormatList } TsOOXMLNumFormatList = class(TsCustomNumFormatList) protected - { procedure AddBuiltinFormats; override; - procedure Analyze(AFormatIndex: Integer; var AFormatString: String; - var ANumFormat: TsNumberFormat; var ADecimals: Word); override; - } public - { - function FormatStringForWriting(AIndex: Integer): String; override; - } + procedure ConvertBeforeWriting(var AFormatString: String; + var ANumFormat: TsNumberFormat); override; end; { TsSpreadOOXMLWriter } @@ -79,6 +74,7 @@ type procedure WriteBorderList(AStream: TStream); procedure WriteFillList(AStream: TStream); procedure WriteFontList(AStream: TStream); + procedure WriteNumFormatList(AStream: TStream); procedure WriteStyleList(AStream: TStream; ANodeName: String); protected { Streams with the contents of files } @@ -115,7 +111,7 @@ type implementation uses - variants; + variants, fpsNumFormatParser; const { OOXML general XML constants } @@ -157,6 +153,81 @@ const MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml'; +{ TsOOXMLNumFormatList } + +{ These are the built-in number formats as expected in the biff spreadsheet file. + Identical to BIFF8. These formats are not written to file but they are used + for lookup of the number format that Excel used. They are specified here in + fpc dialect. } +procedure TsOOXMLNumFormatList.AddBuiltinFormats; +var + fs: TFormatSettings; + cs: String; +begin + fs := Workbook.FormatSettings; + cs := AnsiToUTF8(Workbook.FormatSettings.CurrencyString); + + AddFormat( 0, '', nfGeneral); + AddFormat( 1, '0', nfFixed); + AddFormat( 2, '0.00', nfFixed); + AddFormat( 3, '#,##0', nfFixedTh); + AddFormat( 4, '#,##0.00', nfFixedTh); + AddFormat( 5, '"'+cs+'"#,##0_);("'+cs+'"#,##0)', nfCurrency); + AddFormat( 6, '"'+cs+'"#,##0_);[Red]("'+cs+'"#,##0)', nfCurrencyRed); + AddFormat( 7, '"'+cs+'"#,##0.00_);("'+cs+'"#,##0.00)', nfCurrency); + AddFormat( 8, '"'+cs+'"#,##0.00_);[Red]("'+cs+'"#,##0.00)', nfCurrencyRed); + AddFormat( 9, '0%', nfPercentage); + AddFormat(10, '0.00%', nfPercentage); + AddFormat(11, '0.00E+00', nfExp); + // fraction formats 12 ('# ?/?') and 13 ('# ??/??') not supported + AddFormat(14, fs.ShortDateFormat, nfShortDate); // 'M/D/YY' + AddFormat(15, fs.LongDateFormat, nfLongDate); // 'D-MMM-YY' + AddFormat(16, 'd/mmm', nfCustom); // 'D-MMM' + AddFormat(17, 'mmm/yy', nfCustom); // 'MMM-YY' + AddFormat(18, AddAMPM(fs.ShortTimeFormat, fs), nfShortTimeAM); // 'h:mm AM/PM' + AddFormat(19, AddAMPM(fs.LongTimeFormat, fs), nfLongTimeAM); // 'h:mm:ss AM/PM' + AddFormat(20, fs.ShortTimeFormat, nfShortTime); // 'h:mm' + AddFormat(21, fs.LongTimeFormat, nfLongTime); // 'h:mm:ss' + AddFormat(22, fs.ShortDateFormat + ' ' + fs.ShortTimeFormat, nfShortDateTime); // 'M/D/YY h:mm' (localized) + // 23..36 not supported + AddFormat(37, '_(#,##0_);(#,##0)', nfCurrency); + AddFormat(38, '_(#,##0_);[Red](#,##0)', nfCurrencyRed); + AddFormat(39, '_(#,##0.00_);(#,##0.00)', nfCurrency); + AddFormat(40, '_(#,##0.00_);[Red](#,##0.00)', nfCurrencyRed); + AddFormat(41, '_("'+cs+'"* #,##0_);_("'+cs+'"* (#,##0);_("'+cs+'"* "-"_);_(@_)', nfCustom); + AddFormat(42, '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', nfCustom); + AddFormat(43, '_("'+cs+'"* #,##0.00_);_("'+cs+'"* (#,##0.00);_("'+cs+'"* "-"??_);_(@_)', nfCustom); + AddFormat(44, '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', nfCustom); + AddFormat(45, 'nn:ss', nfCustom); + AddFormat(46, '[h]:nn:ss', nfTimeInterval); + AddFormat(47, 'nn:ss.z', nfCustom); + AddFormat(48, '##0.0E+00', nfCustom); + // 49 ("Text") not supported + + // All indexes from 0 to 163 are reserved for built-in formats. + // The first user-defined format starts at 164. + FFirstFormatIndexInFile := 164; + FNextFormatIndex := 164; +end; + +procedure TsOOXMLNumFormatList.ConvertBeforeWriting(var AFormatString: String; + var ANumFormat: TsNumberFormat); +var + parser: TsNumFormatParser; +begin + parser := TsNumFormatParser.Create(Workbook, AFormatString, ANumFormat); + try + if parser.Status = psOK then begin + // For writing, we have to convert the fpc format string to Excel dialect + AFormatString := parser.FormatString[nfdExcel]; + ANumFormat := parser.NumFormat; + end; + finally + parser.Free; + end; +end; + + { TsSpreadOOXMLWriter } { Adds built-in styles: @@ -452,6 +523,38 @@ begin ''); end; +{ Writes all number formats to the stream. Saving starts at the item with the + FirstFormatIndexInFile. } +procedure TsSpreadOOXMLWriter.WriteNumFormatList(AStream: TStream); +var + i: Integer; + item: TsNumFormatData; + s: String; + n: Integer; + fmt: String; +begin + s := ''; + n := 0; + i := NumFormatList.FindByIndex(NumFormatList.FirstFormatIndexInFile); + if i > -1 then begin + while i < NumFormatList.Count do begin + item := NumFormatList[i]; + if item <> nil then begin + s := s + Format('', + [item.Index, UTF8TextToXMLText(NumFormatList.FormatStringForWriting(i))]); + inc(n); + end; + inc(i); + end; + if n > 0 then + AppendToStream(AStream, Format( + '', [n]), + s, + '' + ); + end; +end; + { Writes the style list which the writer has collected in FFormattingStyles. } procedure TsSpreadOOXMLWriter.WriteStyleList(AStream: TStream; ANodeName: String); var @@ -461,6 +564,7 @@ var numFmtId: Integer; fillId: Integer; borderId: Integer; + idx: Integer; begin AppendToStream(AStream, Format( '<%s count="%d">', [ANodeName, Length(FFormattingStyles)])); @@ -470,8 +574,13 @@ begin sAlign := ''; { Number format } - numFmtId := 0; - s := s + Format('numFmtId="%d" ', [numFmtId]); + if (uffNumberFormat in styleCell.UsedFormattingFields) then begin + idx := NumFormatList.FindFormatOf(@styleCell); + if idx > -1 then begin + numFmtID := NumFormatList[idx].Index; + s := s + Format('numFmtId="%d" applyNumberFormat="1" ', [numFmtId]); + end; + end; { Font } fontId := 0; @@ -586,6 +695,9 @@ begin AppendToStream(FSStyles, Format( '', [SCHEMAS_SPREADML])); + // Number formats + WriteNumFormatList(FSStyles); + // Fonts WriteFontList(FSStyles); @@ -594,16 +706,7 @@ begin // Borders WriteBorderList(FSStyles); - { - AppendToStream(FSStyles, - ''); - AppendToStream(FSStyles, - '', - '', - ''); - AppendToStream(FSStyles, - ''); -} + // Style records AppendToStream(FSStyles, '', @@ -1088,13 +1191,14 @@ procedure TsSpreadOOXMLWriter.WriteNumber(AStream: TStream; const ARow, var CellPosText: String; CellValueText: String; - //S: String; + lStyleIndex: Integer; begin Unused(AStream, ACell); CellPosText := TsWorksheet.CellPosToText(ARow, ACol); CellValueText := Format('%g', [AValue], FPointSeparatorSettings); + lStyleIndex := GetStyleIndex(ACell); AppendToStream(AStream, Format( - '%s', [CellPosText, CellValueText])); + '%s', [CellPosText, lStyleIndex, CellValueText])); end; {*******************************************************************