diff --git a/components/fpspreadsheet/examples/other/chart/piechart_write_demo.lpr b/components/fpspreadsheet/examples/other/chart/piechart_write_demo.lpr index b7fc2ec9f..6b48dd844 100644 --- a/components/fpspreadsheet/examples/other/chart/piechart_write_demo.lpr +++ b/components/fpspreadsheet/examples/other/chart/piechart_write_demo.lpr @@ -49,6 +49,7 @@ begin ser.LabelSeparator := '\n'; // this is the symbol for a line-break ser.LabelPosition := lpOutside; ser.Line.Color := scWhite; + ser.LabelFormat := '#,##0'; //ser.SetFillColorRange(4, 2, 8, 2); b.WriteToFile('world-population.xlsx', true); // Excel fails to open the file diff --git a/components/fpspreadsheet/examples/other/chart/regressionchart_write_demo.lpr b/components/fpspreadsheet/examples/other/chart/regressionchart_write_demo.lpr index a473052ab..1a91452ca 100644 --- a/components/fpspreadsheet/examples/other/chart/regressionchart_write_demo.lpr +++ b/components/fpspreadsheet/examples/other/chart/regressionchart_write_demo.lpr @@ -61,6 +61,7 @@ begin ser.Regression.Equation.Border.Color := scRed; ser.Regression.Equation.Fill.Style := fsSolidFill; ser.Regression.Equation.Fill.FgColor := scSilver; + ser.Regression.Equation.NumberFormat := '0.000'; //ser.Regression.Equation.Top := 5; //ser.Regression.Equation.Left := 5; diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas index 0d7c5d685..5106fda35 100644 --- a/components/fpspreadsheet/source/common/fpschart.pas +++ b/components/fpspreadsheet/source/common/fpschart.pas @@ -132,6 +132,7 @@ type FCaptionRotation: Integer; FLabelFont: TsFont; FLabelFormat: String; + FLabelFormatPercent: String; FLabelRotation: Integer; FLogarithmic: Boolean; FMajorInterval: Double; @@ -158,6 +159,7 @@ type property Inverted: Boolean read FInverted write FInverted; property LabelFont: TsFont read FLabelFont write FLabelFont; property LabelFormat: String read FLabelFormat write FLabelFormat; + property LabelFormatPercent: String read FLabelFormatPercent write FLabelFormatPercent; property LabelRotation: Integer read FLabelRotation write FLabelRotation; property Logarithmic: Boolean read FLogarithmic write FLogarithmic; property MajorGridLines: TsChartLine read FMajorGridLines write FMajorGridLines; @@ -593,6 +595,8 @@ begin FLabelFont.Style := []; FLabelFont.Color := scBlack; + FLabelFormatPercent := '0%'; + FCaptionRotation := 0; FLabelRotation := 0; diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas index 8b3e94a71..c2ec5d29c 100644 --- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas +++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas @@ -18,7 +18,9 @@ type private FSCharts: array of TStream; FSObjectStyles: array of TStream; + FNumberFormatList: TStrings; FPointSeparatorSettings: TFormatSettings; + function GetChartAxisStyleAsXML(Axis: TsChartAxis; AIndent, AStyleID: Integer): String; function GetChartBackgroundStyleAsXML(AChart: TsChart; AFill: TsChartFill; ABorder: TsChartLine; AIndent: Integer; AStyleID: Integer): String; @@ -38,6 +40,9 @@ type function GetChartRegressionStyleAsXML(AChart: TsChart; ASeriesIndex, AIndent, AStyleID: Integer): String; function GetChartSeriesStyleAsXML(AChart: TsChart; ASeriesIndex, AIndent, AStyleID: integer): String; // function GetChartTitleStyleAsXML(AChart: TsChart; AStyleIndex, AIndent: Integer): String; + + function GetNumberFormatID(ANumFormat: String): String; + procedure ListAllNumberFormats(AChart: TsChart); procedure PrepareChartTable(AChart: TsChart; AWorksheet: TsBasicWorksheet); protected @@ -63,6 +68,7 @@ type public constructor Create(AWriter: TsBasicSpreadWriter); override; + destructor Destroy; override; procedure AddChartsToZip(AZip: TZipper); procedure AddToMetaInfManifest(AStream: TStream); procedure CreateStreams; override; @@ -119,6 +125,36 @@ begin Result := Result + Format('_%.2x_', [ord(AName[i])]); end; +{------------------------------------------------------------------------------} +{ internal number formats } +{------------------------------------------------------------------------------} + +type + TsChartNumberFormatList = class(TStringList) + public + constructor Create; + function Add(const ANumFormat: String): Integer; override; + end; + +constructor TsChartNumberFormatList.Create; +begin + inherited; + Add(''); // default number format +end; + +// Adds a new format, but make sure to avoid duplicates. +function TsChartNumberFormatList.Add(const ANumFormat: String): Integer; +begin + if (ANumFormat = '') and (Count > 0) then + Result := 0 + else + begin + Result := IndexOf(ANumFormat); + if Result = -1 then + Result := inherited Add(ANumFormat); + end; +end; + {------------------------------------------------------------------------------} { TsSpreadOpenDocChartWriter } @@ -127,8 +163,17 @@ end; constructor TsSpreadOpenDocChartWriter.Create(AWriter: TsBasicSpreadWriter); begin inherited Create(AWriter); + FPointSeparatorSettings := SysUtils.DefaultFormatSettings; FPointSeparatorSettings.DecimalSeparator:='.'; + + FNumberFormatList := TsChartNumberFormatList.Create; +end; + +destructor TsSpreadOpenDocChartWriter.Destroy; +begin + FNumberFormatList.Free; + inherited; end; procedure TsSpreadOpenDocChartWriter.AddChartsToZip(AZip: TZipper); @@ -201,6 +246,7 @@ var chart: TsChart; indent: String; angle: Integer; + idx: Integer; textProps: String = ''; graphProps: String = ''; chartProps: String = ''; @@ -212,9 +258,11 @@ begin chart := Axis.Chart; - // Special number format for numerical axis labels + // Get number format, use percent format for stacked percentage axis if (Axis = chart.YAxis) and (chart.StackMode = csmStackedPercentage) then - numStyle := 'N10010'; + numStyle := GetNumberFormatID(Axis.LabelFormatPercent) + else + numStyle := GetNumberFormatID(Axis.LabelFormat); // Show axis labels if Axis.ShowLabels then @@ -537,6 +585,7 @@ function TsSpreadOpenDocChartWriter.GetChartRegressionEquationStyleAsXML( AChart: TsChart; AEquation: TsRegressionEquation; AIndent, AStyleID: Integer): String; var indent: String; + idx: Integer; numStyle: String = 'N0'; chartprops: String = ''; lineprops: String = ''; @@ -547,9 +596,7 @@ begin indent := DupeString(' ', AIndent); - // TO DO: Create chart number style list and find the current style there! - if not AEquation.DefaultNumberFormat then - numStyle := 'N0'; + numStyle := GetNumberFormatID(AEquation.NumberFormat); if not AEquation.DefaultXName then chartprops := chartprops + Format('loext:regression-x-name="%s" ', [AEquation.XName]); @@ -647,6 +694,7 @@ var series: TsChartSeries; lineser: TsLineSeries = nil; indent: String; + numStyle: String; chartProps: String = ''; graphProps: String = ''; textProps: String = ''; @@ -659,8 +707,12 @@ begin indent := DupeString(' ', AIndent); series := AChart.Series[ASeriesIndex]; + // Number format + numStyle := GetNumberFormatID(series.LabelFormat); + // Chart properties chartProps := 'chart:symbol-type="none" '; + if (series is TsLineSeries) and (series.ChartType <> ctFilledRadar) then begin lineser := TsLineSeries(series); @@ -672,9 +724,7 @@ begin ); end; - chartProps := chartProps + 'chart:link-data-style-to-source="true" '; - // to do: link-data-style-to-source must go to "false" in case of a specific - // numeric label format. But that requires a number-style in the Object/style.xml + chartProps := chartProps + Format('chart:link-data-style-to-source="%s" ', [FALSE_TRUE[numStyle = 'N0']]); if ([cdlValue, cdlPercentage] * series.DataLabels = [cdlValue]) then chartProps := chartProps + 'chart:data-label-number="value" ' @@ -729,15 +779,63 @@ begin textProps := TsSpreadOpenDocWriter(Writer).WriteFontStyleXMLAsString(series.LabelFont); Result := Format( - indent + '' + LE + + indent + '' + LE + indent + chartProps + LE + indent + ' ' + LE + indent + ' ' + LE + indent + '' + LE, - [ AStyleID, graphProps, textProps ] + [ AStyleID, numstyle, graphProps, textProps ] ); end; +function TsSpreadOpenDocChartWriter.GetNumberFormatID(ANumFormat: String): String; +var + idx: Integer; +begin + idx := FNumberFormatList.IndexOf(ANumFormat); + if idx > -1 then + Result := Format('N%d', [idx]) + else + Result := 'N0'; +end; + +procedure TsSpreadOpenDocChartWriter.ListAllNumberFormats(AChart: TsChart); +var + i: Integer; + series: TsChartSeries; + regression: TsChartRegression; +begin + FNumberFormatList.Clear; + + // Formats of axis labels + FNumberFormatList.Add(AChart.XAxis.LabelFormat); + FNumberFormatList.Add(AChart.YAxis.LabelFormat); + FNumberFormatList.Add(AChart.X2Axis.LabelFormat); + FNumberFormatList.Add(AChart.Y2Axis.LabelFormat); + if AChart.StackMode = csmStackedPercentage then + begin + FNumberFormatList.Add(AChart.YAxis.LabelFormatPercent); + FNumberFormatList.Add(AChart.Y2Axis.LabelFormatPercent); + end; + + // Formats of series labels + for i := 0 to AChart.Series.Count-1 do + begin + series := AChart.Series[i]; + FNumberFormatList.Add(series.LabelFormat); + // Format of fit equation + if (series is TsScatterSeries) then begin + regression := TsScatterSeries(series).Regression; + if (regression.RegressionType <> rtNone) and + (regression.DisplayEquation or regression.DisplayRSquare) then + begin + FNumberFormatList.Add(regression.Equation.NumberFormat); + end; + end; + end; +end; + + { Extracts the cells needed by the given chart from the chart's worksheet and copies their values into a temporary worksheet, AWorksheet, so that these data can be written to the xml immediately. @@ -936,11 +1034,10 @@ var styleStream: TMemoryStream; styleID: Integer; begin -// FChartStyleList.Clear; - chartStream := TMemoryStream.Create; styleStream := TMemoryStream.Create; try + ListAllNumberFormats(AChart); WriteChartNumberStyles(styleStream, 4, AChart); styleID := 1; @@ -1210,9 +1307,28 @@ procedure TsSpreadOpenDocChartWriter.WriteChartNumberStyles(AStream: TStream; AIndent: Integer; AChart: TsChart); var indent: String; + numFmtName: String; + numFmtStr: String; + numFmtXML: String; + i: Integer; + parser: TsSpreadOpenDocNumFormatParser; begin indent := DupeString(' ', AIndent); + for i := 0 to FNumberFormatList.Count-1 do begin + numFmtName := Format('N%d', [i]); + numFmtStr := FNumberFormatList[i]; + parser := TsSpreadOpenDocNumFormatParser.Create(numFmtStr, FWriter.Workbook.FormatSettings); + try + numFmtXML := parser.BuildXMLAsString(numFmtName); + if numFmtXML <> '' then + AppendToStream(AStream, indent + numFmtXML); + finally + parser.Free; + end; + end; + { + AppendToStream(AStream, indent + '' + LE + indent + ' ' + LE + @@ -1226,6 +1342,7 @@ begin indent + ' %' + LE + indent + '' + LE ); + } end; { Writes the file "Object N/styles.xml" (N = 1, 2, ...) which is needed by the