diff --git a/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpi b/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpi
new file mode 100644
index 000000000..8a52ac6e3
--- /dev/null
+++ b/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpi
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
diff --git a/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpr b/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpr
new file mode 100644
index 000000000..33ac20548
--- /dev/null
+++ b/components/fpspreadsheet/examples/other/chart/linechart_write_demo.lpr
@@ -0,0 +1,128 @@
+program linechart_write_demo;
+
+{.$DEFINE DARK_MODE}
+
+uses
+ SysUtils,
+ fpspreadsheet, fpstypes, fpsUtils, fpschart, xlsxooxml, fpsopendocument;
+
+procedure WriteHelp;
+begin
+ WriteLn('SYNTAX: linechart_write_demo [rotated] [normal|stacked|percent-stacked] ');
+ WriteLn(' (no argument) ..... normal orientation (x horizontal)');
+ WriteLn(' rotated ........... axes rotated (x vertical)');
+ WriteLn(' normal ............ back-to-bottom areas (default)');
+ WriteLn(' stacked ........... stacked areas');
+ WriteLn(' percent-stacked ... stacked by percentage');
+ Halt;
+end;
+
+const
+ FILE_NAME = 'line';
+var
+ book: TsWorkbook;
+ sheet: TsWorksheet;
+ ch: TsChart;
+ ser: TsLineSeries;
+ dir, fn: String;
+ stackMode: TsChartStackMode = csmDefault;
+ rotated: Boolean = false;
+ i: Integer;
+begin
+ fn := FILE_NAME;
+
+ for i := 1 to ParamCount do
+ case lowercase(ParamStr(i)) of
+ 'rotated':
+ rotated := true;
+ 'normal' ,'default':
+ stackMode := csmDefault;
+ 'stacked':
+ stackMode := csmStacked;
+ 'percent-stacked', 'stacked-percent', 'percentstacked', 'stackedpercent', 'percent', 'percentage':
+ stackMode := csmStackedPercentage;
+ else
+ WriteHelp;
+ end;
+
+ if rotated then
+ fn := fn + '-rotated';
+ case stackMode of
+ csmDefault: ;
+ csmStacked: fn := fn + '-stacked';
+ csmStackedPercentage: fn := fn + '-stackedpercent';
+ end;
+
+ dir := ExtractFilePath(ParamStr(0)) + 'files/';
+ ForceDirectories(dir);
+
+ book := TsWorkbook.Create;
+ try
+ // worksheet
+ sheet := book.AddWorksheet('line_series');
+
+ // Enter data
+ sheet.WriteText( 0, 0, 'School Grades');
+ sheet.WriteFont( 0, 0, '', 12, [fssBold], scBlack);
+ sheet.WriteText( 2, 0, ''); sheet.WriteText ( 2, 1, 'Student 1'); sheet.WriteText ( 2, 2, 'Student 2');
+ sheet.WriteText( 3, 0, 'Biology'); sheet.WriteNumber( 3, 1, 12); sheet.WriteNumber( 3, 2, 15);
+ sheet.WriteText( 4, 0, 'History'); sheet.WriteNumber( 4, 1, 11); sheet.WriteNumber( 4, 2, 13);
+ sheet.WriteText( 5, 0, 'French'); sheet.WriteNumber( 5, 1, 16); sheet.WriteNumber( 5, 2, 11);
+ sheet.WriteText( 6, 0, 'English'); sheet.WriteNumber( 6, 1, 18); sheet.WriteNumber( 6, 2, 11);
+ sheet.WriteText( 7, 0, 'Sports'); sheet.WriteNumber( 7, 1, 16); sheet.WriteNumber( 7, 2, 7);
+ sheet.WriteText( 8, 0, 'Maths'); sheet.WriteNumber( 8, 1, 10); sheet.WriteNumber( 8, 2, 17);
+ sheet.WriteText( 9, 0, 'Physics'); sheet.WriteNumber( 9, 1, 12); sheet.WriteNumber( 9, 2, 19);
+ sheet.WriteText(10, 0, 'Computer'); sheet.WriteNumber(10, 1, 16); sheet.WriteNumber(10, 2, 18);
+
+ // Create chart: left/top in cell D4, 160 mm x 100 mm
+ ch := book.AddChart(sheet, 2, 3, 160, 100);
+
+ // Chart properties
+ ch.Border.Style := clsNoLine;
+ ch.Title.Caption := 'School Grades';
+ ch.Title.Font.Style := [fssBold];
+ ch.Title.Font.Color := scBlue;
+ ch.Legend.Border.Style := clsNoLine;
+ ch.XAxis.Title.Caption := '';
+ ch.YAxis.Title.Caption := 'Grade points';
+ ch.YAxis.AxisLine.Color := scSilver;
+ ch.YAxis.MajorTicks := [];
+ ch.RotatedAxes := rotated;
+ ch.StackMode := stackMode;
+
+ // Add 1st line series ("Student 1")
+ ser := TsLineSeries.Create(ch);
+ ser.SetTitleAddr(2, 1); // series 1 title in cell B3
+ ser.SetLabelRange(3, 0, 10, 0); // series 1 x labels in A4:A11
+ ser.SetYRange(3, 1, 10, 1); // series 1 y values in B4:B11
+ ser.Line.Color := scRed;
+ ser.ShowSymbols := true;
+ ser.SymbolFill.Color := scRed;
+ ser.SymbolFill.Style := cfsSolid;
+ ser.SymbolBorder.Color := scBlack;
+ ser.Smooth := true;
+// ser.GroupIndex := -1;
+
+ // Add 2nd line series ("Student 2")
+ ser := TsLineSeries.Create(ch);
+ ser.SetTitleAddr(2, 2); // series 2 title in cell C3
+ ser.SetLabelRange(3, 0, 10, 0); // series 2 x labels in A4:A11
+ ser.SetYRange(3, 2, 10, 2); // series 2 y values in C4:C11
+ ser.Line.Color := scBlue;
+ ser.SymbolFill.Color := scBlue;
+ ser.SymbolFill.Style := cfsSolid;
+ ser.SymbolBorder.Color := scBlack;
+ //ser.Smooth := true;
+ ser.ShowSymbols := true;
+// ser.GroupIndex := -1;
+
+ book.WriteToFile(dir + fn + '.xlsx', true);
+ WriteLn('... ', fn + '.xlsx');
+
+ book.WriteToFile(dir + fn + '.ods', true);
+ WriteLn('... ', fn + '.ods');
+ finally
+ book.Free;
+ end;
+end.
+
diff --git a/components/fpspreadsheet/examples/other/chart/run_write_demos.bat b/components/fpspreadsheet/examples/other/chart/run_write_demos.bat
index 3973ef869..662de180b 100644
--- a/components/fpspreadsheet/examples/other/chart/run_write_demos.bat
+++ b/components/fpspreadsheet/examples/other/chart/run_write_demos.bat
@@ -20,13 +20,21 @@ barchart_write_demo horiz stacked
barchart_write_demo vert percentage
barchart_write_demo horiz percentage
echo.
-echoe BAR SERIES WITH TWO AXES
+echo BAR SERIES WITH TWO AXES
barchart_2axes_write_demo
barchart_2axes_write_demo rotated
echo.
echo BUBBLE SERIES
bubblechart_write_demo
echo.
+echo LINE SERIES
+linechart_write_demo
+linechart_write_demo stacked
+linechart_write_demo percentage
+linechart_write_demo rotated
+linechart_write_demo stacked rotated
+linechart_write_demo percentage rotated
+echo.
echo PIE SERIES
piechart_write_demo
piechart_write_demo ring
diff --git a/components/fpspreadsheet/examples/other/chart/write_demos.lpg b/components/fpspreadsheet/examples/other/chart/write_demos.lpg
index 1c328c638..70a6998f4 100644
--- a/components/fpspreadsheet/examples/other/chart/write_demos.lpg
+++ b/components/fpspreadsheet/examples/other/chart/write_demos.lpg
@@ -22,6 +22,11 @@
+
+
+
+
+
diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas
index a5a99c481..9c3e0d35a 100644
--- a/components/fpspreadsheet/source/common/fpschart.pas
+++ b/components/fpspreadsheet/source/common/fpschart.pas
@@ -177,6 +177,7 @@ type
TsChartFillStyle = (cfsNoFill, cfsSolid, cfsGradient, cfsHatched, cfsSolidHatched, cfsImage);
TsChartFill = class
+ public
Style: TsChartFillStyle;
Color: TsColor;
Gradient: Integer; // Index into chart's Gradients list
@@ -621,8 +622,15 @@ type
cssDash, cssDot
);
+ TsChartInterpolation = (
+ ciLinear,
+ ciCubicSpline, ciBSpline,
+ ciStepStart, ciStepEnd, ciStepCenterX, ciStepCenterY
+ );
+
TsCustomLineSeries = class(TsChartSeries)
private
+ FInterpolation: TsChartInterpolation;
FSymbol: TsChartSeriesSymbol;
FSymbolHeight: Double; // in mm
FSymbolWidth: Double; // in mm
@@ -630,7 +638,11 @@ type
FShowSymbols: Boolean;
FSymbolBorder: TsChartLine;
FSymbolFill: TsChartFill;
+ function GetSmooth: Boolean;
+ procedure SetSmooth(AValue: Boolean);
protected
+ property Interpolation: TsChartInterpolation read FInterpolation write FInterpolation;
+ property Smooth: Boolean read GetSmooth write SetSmooth;
property Symbol: TsChartSeriesSymbol read FSymbol write FSymbol;
property SymbolBorder: TsChartLine read FSymbolBorder write FSymbolBorder;
property SymbolFill: TsChartFill read FSymbolFill write FSymbolFill;
@@ -645,6 +657,9 @@ type
TsLineSeries = class(TsCustomLineSeries)
public
+ constructor Create(AChart: TsChart); override;
+ property Interpolation;
+ property Smooth;
property Symbol;
property SymbolBorder;
property SymbolFill;
@@ -691,6 +706,8 @@ type
TsScatterSeries = class(TsCustomScatterSeries)
public
+ property Interpolation;
+ property Smooth;
property Symbol;
property SymbolBorder;
property SymbolFill;
@@ -761,11 +778,6 @@ type
end;
TsChartStackMode = (csmDefault, csmStacked, csmStackedPercentage);
- TsChartInterpolation = (
- ciLinear,
- ciCubicSpline, ciBSpline,
- ciStepStart, ciStepEnd, ciStepCenterX, ciStepCenterY
- );
TsChart = class(TsChartFillElement)
private
@@ -2550,6 +2562,30 @@ begin
inherited;
end;
+function TsCustomLineSeries.GetSmooth: Boolean;
+begin
+ Result := FInterpolation in [ciBSpline, ciCubicSpline];
+end;
+
+procedure TsCustomLineSeries.SetSmooth(AValue: Boolean);
+begin
+ if AValue then
+ begin
+ if not (FInterpolation in [ciBSpline, ciCubicSpline]) then
+ FInterpolation := ciCubicSpline;
+ end else
+ begin
+ if (FInterpolation in [ciBSpline, ciCubicSpline]) then
+ FInterpolation := ciLinear;
+ end;
+end;
+
+{ TsLineSeries }
+constructor TsLineSeries.Create(AChart: TsChart);
+begin
+ inherited Create(AChart);
+ FGroupIndex := 0;
+end;
{ TsPieSeries }
constructor TsPieSeries.Create(AChart: TsChart);
diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
index 14eadf8d3..66c2af17a 100644
--- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
@@ -173,7 +173,10 @@ uses
type
TAxisKind = 3..6;
- TsOpenedCustomLineSeries = class(TsCustomLineSeries);
+ TsOpenedCustomLineSeries = class(TsCustomLineSeries)
+ public
+ property Interpolation;
+ end;
TsOpenedTrendlineSeries = class(TsChartSeries)
public
@@ -2603,6 +2606,7 @@ function TsSpreadOpenDocChartWriter.GetChartPlotAreaStyleAsXML(AChart: TsChart;
AIndent, AStyleID: Integer): String;
var
indent: String;
+ interpolation: TsChartInterpolation;
interpolationStr: String = '';
verticalStr: String = '';
stackModeStr: String = '';
@@ -2625,15 +2629,24 @@ begin
if (AChart.Series.Count > 0) and (AChart.Series[0] is TsPieSeries) then
startAngleStr := Format('chart:angle-offset="%d" ', [TsPieSeries(AChart.Series[0]).StartAngle]);
- case AChart.Interpolation of
- ciLinear: ;
- ciCubicSpline: interpolationStr := 'chart:interpolation="cubic-spline" ';
- ciBSpline: interpolationStr := 'chart:interpolation="b-spline" ';
- ciStepStart: interpolationStr := 'chart:interpolation="step-start" ';
- ciStepEnd: interpolationStr := 'chart:interpolation="step-end" ';
- ciStepCenterX: interpolationStr := 'chart:interpolation="step-center-x" ';
- ciStepCenterY: interpolationStr := 'chart:interpolation="step-center-y" ';
- end;
+ // In FPSpreadsheet individual series can be "smooth", in Calc only all.
+ // As a compromise, when we find at least one smooth series, all series are
+ // treated as such by writing the "chart:interpolation" attribute
+ for i := 0 to AChart.Series.Count-1 do
+ if AChart.Series[i] is TsCustomLineSeries then
+ begin
+ interpolation := TsOpenedCustomLineSeries(AChart.Series[i]).Interpolation;
+ case interpolation of
+ ciLinear: Continue;
+ ciCubicSpline: interpolationStr := 'chart:interpolation="cubic-spline" ';
+ ciBSpline: interpolationStr := 'chart:interpolation="b-spline" ';
+ ciStepStart: interpolationStr := 'chart:interpolation="step-start" ';
+ ciStepEnd: interpolationStr := 'chart:interpolation="step-end" ';
+ ciStepCenterX: interpolationStr := 'chart:interpolation="step-center-x" ';
+ ciStepCenterY: interpolationStr := 'chart:interpolation="step-center-y" ';
+ end;
+ break;
+ end;
if not (AChart.GetChartType in [ctRadar, ctFilledRadar, ctPie]) then
rightAngledAxes := 'chart:right-angled-axes="true" ';
diff --git a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
index 5fe49ac47..176e48b71 100644
--- a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
+++ b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
@@ -114,6 +114,7 @@ type
procedure WriteAreaSeries(AStream: TStream; AIndent: Integer; ASeries: TsAreaSeries; ASeriesIndex, APosInAxisGroup: Integer);
procedure WriteBarSeries(AStream: TStream; AIndent: Integer; ASeries: TsBarSeries; ASeriesIndex, APosInAxisGroup: Integer);
procedure WriteBubbleSeries(AStream: TStream; AIndent: Integer; ASeries: TsBubbleSeries; ASeriesIndex, APosInAxisGroup: Integer);
+ procedure WriteLineSeries(AStream: TStream; AIndent: Integer; ASeries: TsLineSeries; ASeriesIndex, APosInAxisGroup: Integer);
procedure WritePieSeries(AStream: TStream; AIndent: Integer; ASeries: TsPieSeries; ASeriesIndex: Integer);
procedure WriteRadarSeries(AStream: TStream; AIndent: Integer; ASeries: TsRadarSeries; ASeriesIndex: Integer);
procedure WriteScatterSeries(AStream: TStream; AIndent: Integer; ASeries: TsScatterSeries; ASeriesIndex, APosInAxisGroup: Integer);
@@ -1280,6 +1281,7 @@ begin
while Assigned(ANode) do
begin
nodeName := ANode.NodeName;
+ s := GetAttrValue(ANode, 'val');
case nodeName of
'c:ser':
begin
@@ -1287,19 +1289,14 @@ begin
ReadChartSeriesProps(ANode.FirstChild, ser);
end;
'c:grouping':
- begin
- s := GetAttrValue(ANode, 'val');
- case s of
- 'stacked': AChart.StackMode := csmStacked;
- 'percentStacked': AChart.StackMode := csmStackedPercentage;
- end;
+ case s of
+ 'stacked': AChart.StackMode := csmStacked;
+ 'percentStacked': AChart.StackMode := csmStackedPercentage;
end;
'c:varyColors':
;
'c:dLbls':
;
- 'c:gapWidth':
- ;
{
'c:axId':
ReadChartSeriesAxis(ANode, ser);
@@ -1481,14 +1478,6 @@ begin
1: ReadChartAxis(workNode.FirstChild, AChart, AChart.X2Axis, FX2AxisID, FX2AxisDelete);
end;
inc(catAxCounter);
- {
- if (AChart.X2Axis.Alignment = AChart.XAxis.Alignment) and FX2AxisDelete then
- begin
- // Force using only a single x axis in this case
- FX2AxisID := FXAxisID;
- AChart.X2Axis.Visible := false;
- end;
- }
end;
'c:dateAx':
begin
@@ -1503,14 +1492,6 @@ begin
end;
end;
inc(dateAxCounter);
- {
- if (dateAxCounter > 1) and (AChart.X2Axis.Alignment = AChart.XAxis.Alignment) and FX2AxisDelete then
- begin
- // Force using only a single x axis in this case.
- FX2AxisID := FXAxisID;
- AChart.X2Axis.Visible := false;
- end;
- }
end;
'c:valAx':
begin
@@ -1527,14 +1508,6 @@ begin
2: ReadChartAxis(workNode.FirstChild, AChart, AChart.Y2Axis, FY2AxisID, FY2AxisDelete);
3: ReadChartAxis(workNode.FirstChild, AChart, AChart.X2Axis, FX2AxisID, FX2AxisDelete);
end;
- {
- if (AChart.X2Axis.Alignment = AChart.XAxis.Alignment) and FX2AxisDelete then
- begin
- // Force using only a single x axis in this case.
- FX2AxisID := FXAxisID;
- AChart.X2Axis.Visible := false;
- end;
- }
end else
begin
case valAxCounter of
@@ -1665,6 +1638,7 @@ var
nodeName: String;
s: String;
ser: TsScatterSeries;
+ smooth: Boolean = false;
begin
if ANode = nil then
exit;
@@ -1681,9 +1655,10 @@ begin
begin
s := GetAttrValue(ANode, 'val');
if (s = 'smoothMarker') then
- AChart.Interpolation := ciCubicSpline;
+ smooth := true;
end;
- 'c:varyColors': ;
+ 'c:varyColors':
+ ;
'c:dLbls':
;
'c:axId':
@@ -1691,6 +1666,8 @@ begin
end;
ANode := ANode.NextSibling;
end;
+
+ ser.Smooth := smooth;
end;
{@@ ----------------------------------------------------------------------------
@@ -1971,6 +1948,7 @@ var
nodeName, s: String;
n: Integer;
idx: Integer;
+ smooth: Boolean = false;
begin
if ANode = nil then
exit;
@@ -2011,6 +1989,8 @@ begin
ReadChartSeriesTrendLine(ANode.FirstChild, ASeries);
'c:errBars':
ReadChartSeriesErrorBars(ANode.FirstChild, ASeries);
+ 'c:smooth':
+ smooth := (s <> '0');
'c:invertIfNegative':
;
'c:extLst':
@@ -2018,6 +1998,9 @@ begin
end;
ANode := ANode.NextSibling;
end;
+
+ if ASeries is TsCustomLineSeries then
+ TsOpenedCustomLineSeries(ASeries).Smooth := smooth;
end;
{@@ ----------------------------------------------------------------------------
@@ -4028,25 +4011,28 @@ begin
chart := ASeries.Chart;
ser := TsOpenedCustomLineSeries(ASeries);
- if ser.ShowSymbols then
- case ser.Symbol of
- cssRect: markerStr := 'square';
- cssDiamond: markerStr := 'diamond';
- cssTriangle: markerStr := 'triangle';
- cssTriangleDown: markerStr := 'triangle'; // !!!!
- cssTriangleLeft: markerStr := 'triangle'; // !!!!
- cssTriangleRight: markerStr := 'triangle'; // !!!!
- cssCircle: markerStr := 'circle';
- cssStar: markerStr := 'star';
- cssX: markerstr := 'x';
- cssPlus: markerStr := '+';
- cssAsterisk: markerStr := 'star'; // !!!
- cssDash: markerStr := 'dash';
- cssDot: markerStr := 'dot';
- else markerStr := 'star'; // !!!
- end // The symbols marked by !!! are not available in Excel
- else
- markerStr := 'none';
+ if not ser.ShowSymbols then
+ begin
+ Result := indent + '';
+ exit;
+ end;
+
+ case ser.Symbol of
+ cssRect: markerStr := 'square';
+ cssDiamond: markerStr := 'diamond';
+ cssTriangle: markerStr := 'triangle';
+ cssTriangleDown: markerStr := 'triangle'; // !!!!
+ cssTriangleLeft: markerStr := 'triangle'; // !!!!
+ cssTriangleRight: markerStr := 'triangle'; // !!!!
+ cssCircle: markerStr := 'circle';
+ cssStar: markerStr := 'star';
+ cssX: markerstr := 'x';
+ cssPlus: markerStr := '+';
+ cssAsterisk: markerStr := 'star'; // !!!
+ cssDash: markerStr := 'dash';
+ cssDot: markerStr := 'dot';
+ else markerStr := 'star'; // !!!
+ end; // The symbols marked by !!! are not available in Excel
symbolSizePts := round(mmToPts((ser.SymbolWidth + ser.SymbolHeight)/2));
if symbolSizePts > 72 then symbolSizePts := 72;
@@ -4190,6 +4176,8 @@ begin
WriteBubbleSeries(AStream, AIndent + 2, TsBubbleSeries(ser), j, posInGroup);
xAxKind := 'c:valAx';
end;
+ ctLine:
+ WriteLineSeries(AStream, AIndent + 2, TsLineSeries(ser), j, posInGroup);
ctPie, ctRing:
WritePieSeries(AStream, AIndent + 2, TsPieSeries(ser), j);
ctRadar, ctFilledRadar:
@@ -4573,12 +4561,11 @@ begin
indent + ' ' + LE
);
end;
- if TsOpenedCustomLineSeries(ASeries).ShowSymbols then
- AppendToStream(AStream,
- indent + ' ' + LE +
- GetChartSeriesMarkerXML(AIndent + 4, TsOpenedCustomLineSeries(ASeries)) + LE +
- indent + ' ' + LE
- );
+ AppendToStream(AStream,
+ indent + ' ' + LE +
+ GetChartSeriesMarkerXML(AIndent + 4, TsOpenedCustomLineSeries(ASeries)) + LE +
+ indent + ' ' + LE
+ );
end else
// Series main formatting
AppendToStream(AStream,
@@ -4629,9 +4616,9 @@ begin
[ yValName ]
));
+ // Bubble series: Bubble size range
if (ASeries is TsBubbleSeries) then
begin
- // Bubble size range
AppendToStream(AStream,
indent + '' + LE +
indent + GetChartRangeXML(AIndent + 4, TsBubbleSeries(ASeries).BubbleRange, 'c:numRef') + LE +
@@ -4639,6 +4626,13 @@ begin
);
end;
+ // Line series: Interpolation
+ if ASeries is TsLineSeries then
+ if TsLineSeries(ASeries).Interpolation in [ciLinear, ciStepStart, ciStepEnd, ciStepCenterX, ciStepCenterY] then
+ AppendToStream(AStream,
+ indent + ' ' + LE
+ );
+
AppendToStream(AStream,
indent + '' + LE
);
@@ -4734,6 +4728,67 @@ begin
);
end;
+{@@ ----------------------------------------------------------------------------
+ Writes the properties of the given line series to the node of
+ file chartN.xml
+-------------------------------------------------------------------------------}
+procedure TsSpreadOOXMLChartWriter.WriteLineSeries(AStream: TStream;
+ AIndent: Integer; ASeries: TsLineSeries; ASeriesIndex, APosInAxisGroup: Integer);
+const
+ GROUPING: Array[TsChartStackMode] of string = ('standard', 'stacked', 'percentStacked');
+var
+ indent: String;
+ chart: TsChart;
+ xAxis: TsChartAxis;
+ isFirstOfGroup: Boolean;
+ isLastOfGroup: Boolean;
+ prevSeriesGroupIndex: Integer = -1;
+ nextSeriesGroupIndex: Integer = -1;
+begin
+ indent := DupeString(' ', AIndent);
+ chart := ASeries.Chart;
+
+ if (ASeriesIndex > 0) and (chart.Series[ASeriesIndex-1].YAxis = ASeries.YAxis) then
+ prevSeriesGroupIndex := chart.Series[ASeriesIndex-1].GroupIndex;
+ if (ASeriesIndex < chart.Series.Count-1) and (chart.Series[ASeriesIndex+1].YAxis = ASeries.YAxis) then
+ nextSeriesGroupIndex := chart.Series[ASeriesIndex+1].GroupIndex;
+
+ isFirstOfGroup := APosInAxisGroup and 1 = 1;
+ isLastOfGroup := APosInAxisgroup and 2 = 2;
+
+ if ((ASeries.GroupIndex > -1) and (prevSeriesGroupIndex = ASeries.GroupIndex)) then
+ isFirstOfGroup := false;
+ if ((ASeries.GroupIndex > -1) and (nextSeriesGroupIndex = ASeries.GroupIndex)) then
+ isLastOfGroup := false;
+
+ if isFirstOfGroup then
+ AppendToStream(AStream, Format(
+ indent + '' + LE +
+ indent + ' ' + LE,
+ [ GROUPING[chart.StackMode] ]
+ ));
+
+ WriteChartSeriesNode(AStream, AIndent + 2, ASeries, ASeriesIndex);
+
+ if isLastOfGroup then
+ begin
+ if ASeries.YAxis = calPrimary then
+ xAxis := chart.XAxis
+ else
+ xAxis := chart.X2Axis;
+
+ AppendToStream(AStream, Format(
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + '' + LE,
+ [
+ FAxisID[xAxis.Alignment], //
+ FAxisID[ASeries.GetYAxis.Alignment] //
+ ]
+ ));
+ end;
+end;
+
{$ENDIF}
end.
diff --git a/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas b/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas
index e38955d7a..e57cbcae5 100644
--- a/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas
+++ b/components/fpspreadsheet/source/visual/fpspreadsheetchart.pas
@@ -19,7 +19,7 @@ interface
{$ifdef FPS_CHARTS}
-uses
+uses lazloggerbase,
// RTL/FCL
Classes, Contnrs, SysUtils, Types, FPCanvas,
// LCL
@@ -1079,7 +1079,6 @@ procedure TsWorkbookChartSource.UseDataPointColors(ASeries: TsChartSeries);
var
datapointStyle: TsChartDataPointStyle;
- style: TChartStyle;
i, j: Integer;
c: TsColor;
g: TsChartGradient;
@@ -1187,6 +1186,7 @@ var
firstSeries: TChartSeries;
ch: TsChart;
src: TsWorkbookChartSource;
+ interpolation: TsChartInterpolation = ciLinear;
calcSrc: TCalculatedChartSource;
style: TChartStyle;
axAlign: TChartAxisAlignment;
@@ -1246,6 +1246,14 @@ begin
src := TsWorkbookChartSource.Create(self);
src.WorkbookSource := FWorkbookSource;
+ if (ASeries is TsCustomLineSeries) then
+ begin
+ interpolation := TsOpenedCustomLineSeries(ASeries).Interpolation;
+ // TAChart cannot stack spline series.
+ if (interpolation in [ciBSpline, ciCubicSpline]) and (ch.StackMode <> csmDefault) then
+ interpolation := ciLinear;
+ end;
+
case ASeries.ChartType of
ctBar:
begin
@@ -1253,11 +1261,11 @@ begin
src.IntegerX := true;
end;
ctLine, ctScatter:
- case ch.Interpolation of
+ case interpolation of
ciLinear, ciStepStart, ciStepEnd, ciStepCenterX, ciStepCenterY:
begin
Result := TLineSeries.Create(FChart);
- case ch.Interpolation of
+ case interpolation of
ciLinear: TLineSeries(Result).LineType := ltFromPrevious;
ciStepStart: TLineSeries(Result).LineType := ltStepXY;
ciStepEnd: TLineSeries(Result).LineType := ltStepYX;
@@ -1890,26 +1898,33 @@ end;
function TsWorkbookChartLink.IsStackable(ASeries: TsChartSeries): Boolean;
var
ch: TsChart;
+ ser: TsChartSeries;
i, numSeries: Integer;
begin
- Result := (ASeries.ChartType in [ctBar, ctLine, ctArea]);
+ Result := (ASeries.ChartType in [ctBar, ctLine, ctArea]) and (ASeries.GroupIndex > -1);
if Result then
begin
ch := ASeries.Chart;
numSeries := ch.Series.Count;
if numSeries = 1 then
+ begin
+ Result := false;
exit;
+ end;
// Check whether all series are the same type and same y axis as ASeries.
// NOTE: Not perfect yet since there might abe two stackable groups,
// one for the left and one for the right axis...
for i := 0 to numSeries - 1 do
- if (ch.Series[i].ChartType <> ASeries.ChartType) or
- (ch.Series[i].YAxis <> ASeries.YAxis) then
+ begin
+ ser := ch.Series[i];
+ if (ser.ChartType <> ASeries.ChartType) or (ser.GroupIndex <> ASeries.GroupIndex) or
+ (ser.YAxis <> ASeries.YAxis) then
begin
Result := false;
exit;
end;
+ end;
end;
end;
@@ -2661,10 +2676,15 @@ procedure TsWorkbookChartLink.UpdateChartStyle(AWorkbookSeries: TsChartSeries;
AStyleIndex: Integer);
var
style: TChartStyle;
+ ch: TsChart;
begin
+ ch := AWorkbookSeries.Chart;
style := TChartStyle(FChartStyles.Styles[AStyleIndex]);
- UpdateChartPen(AWorkbookSeries.Chart, AWorkbookSeries.Line, style.Pen);
- UpdateChartBrush(AWorkbookSeries.Chart, AWorkbookSeries.Fill, style.Brush);
+ UpdateChartPen(ch, AWorkbookSeries.Line, style.Pen);
+ if (AWorkbookSeries is TsCustomLineSeries) then
+ UpdateChartBrush(ch, TsOpenedCustomLineSeries(AWorkbookSeries).SymbolFill, style.Brush)
+ else
+ UpdateChartBrush(ch, AWorkbookSeries.Fill, style.Brush);
end;
{@@ Updates title and footer of the linked TAChart.
@@ -2731,6 +2751,9 @@ begin
seriesPointer.HorizSize := mmToPx(openedWorkbookSeries.SymbolWidth / 2, ppi);
seriesPointer.VertSize := mmToPx(openedWorkbookSeries.SymbolHeight / 2, ppi);
+ if lineseries <> nil then
+ DebugLn([BoolToStr(lineseries.ShowPoints, true), ' ', intTostr(ord(lineseries.pointer.Style)), ' ', inttostr(ord(lineseries.pointer.brush.style))]);
+
// Error bars
UpdateChartErrorBars(AWorkbookSeries, AChartSeries);