diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas
index 96c5bd941..021629922 100644
--- a/components/fpspreadsheet/source/common/fpschart.pas
+++ b/components/fpspreadsheet/source/common/fpschart.pas
@@ -57,7 +57,7 @@ type
procedure CopyFrom(ALine: TsChartLine);
end;
- TsChartGradientStyle = (cgsLinear, cgsAxial, cgsRadial, cgsElliptic, cgsSquare, cgsRectangular);
+ TsChartGradientStyle = (cgsLinear, cgsAxial, cgsRadial, cgsElliptic, cgsSquare, cgsRectangular, cgsShape);
TsChartGradientStep = record
Value: Double; // 0.0 ... 1.0
@@ -78,8 +78,8 @@ type
Name: String;
Style: TsChartGradientStyle;
Border: Double; // 0.0 ... 1.0
- CenterX, CenterY: Double; // 0.0 ... 1.0
- Angle: Double; // degrees
+ CenterX, CenterY: Double; // 0.0 ... 1.0 ( for gradients which are not linear )
+ Angle: Double; // degrees, 0° = horizontal, grows CCW
constructor Create;
destructor Destroy; override;
procedure CopyFrom(ASource: TsChartGradient);
diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
index 153108fc8..32f3d271f 100644
--- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas
@@ -202,7 +202,7 @@ const
); // unsupported: bow-tie, hourglass, vertical-bar
GRADIENT_STYLES: array[TsChartGradientStyle] of string = (
- 'linear', 'axial', 'radial', 'ellipsoid', 'square', 'rectangular'
+ 'linear', 'axial', 'radial', 'ellipsoid', 'square', 'rectangular', 'radial'
);
HATCH_STYLES: array[TsChartHatchStyle] of string = (
@@ -2020,6 +2020,9 @@ begin
for i := Length(s) downto 1 do
if not (s[i] in ['0'..'9', '.', '+', '-']) then Delete(s, i, 1);
angle := StrToFloatDef(s, 0.0, FPointSeparatorSettings);
+ { ods has angle=0 in vertical direction, and orientation is CW
+ --> We must transform to fps angular orientations (0° horizontal, CCW) }
+ angle := (90.0 - angle) mod 360;
end;
s := GetAttrValue(ANode, 'draw:cx');
@@ -3539,7 +3542,7 @@ begin
cgsLinear, cgsAxial:
style := style + Format(
'draw:angle="%.0fdeg" ',
- [ gradient.Angle ],
+ [ (90 - gradient.Angle) mod 360 ], // transform to fps angle orientations
FPointSeparatorSettings
);
cgsElliptic, cgsSquare, cgsRectangular:
@@ -3548,12 +3551,14 @@ begin
[ gradient.CenterX * 100, gradient.CenterY * 100, gradient.Angle ],
FPointSeparatorSettings
);
- cgsRadial:
+ cgsRadial, cgsShape:
style := style + Format(
'draw:cx="%.0f%%" draw:cy="%.0f%%" ',
[ gradient.CenterX * 100, gradient.CenterY * 100 ],
FPointSeparatorSettings
);
+ else
+ raise Exception.Create('Unsupported gradient style');
end;
style := style + '/>' + LE;
diff --git a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
index 65a519c86..e7fef5adb 100644
--- a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
+++ b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas
@@ -83,6 +83,7 @@ type
FAxisID: array[TsChartAxisAlignment] of DWord;
FSeriesIndex: Integer;
function GetChartColorXML(AIndent: Integer; ANodeName: String; AColor: TsChartColor): String;
+ function GetChartColorXML(AColor: TsChartColor): String;
function GetChartFillAndLineXML(AIndent: Integer; AChart: TsChart; AFill: TsChartFill; ALine: TsChartLine): String;
function GetChartFillXML(AIndent: Integer; AChart: TsChart; AFill: TsChartFill): String;
function GetChartFontXML(AIndent: Integer; AFont: TsFont; ANodeName: String): String;
@@ -779,6 +780,26 @@ begin
if TryStrToFloat(s, value, FPointSeparatorSettings) then
gradient.Angle := value / ANGLE_MULTIPLIER;
end;
+ 'a:path':
+ begin
+ s := GetAttrValue(ANode, 'path');
+ case s of
+ 'rect': gradient.Style := cgsRectangular;
+ 'circle': gradient.Style := cgsRadial;
+ 'shape': gradient.Style := cgsShape;
+ end;
+ child := ANode.FindNode('a:fillToRect');
+ s := GetAttrValue(ANode, 'l');
+ if TryStrToFloat(s, value, FPointSeparatorSettings) then
+ gradient.CenterX := value / FACTOR_MULTIPLIER
+ else
+ gradient.CenterX := 0.0;
+ s := GetAttrValue(aNode, 't');
+ if tryStrToFloat(s, value, FPointSeparatorSettings) then
+ gradient.CenterY := value / FACTOR_MULTIPLIER
+ else
+ gradient.CenterY := 0.0;
+ end;
end;
ANode := ANode.NextSibling;
end;
@@ -3403,27 +3424,30 @@ function TsSpreadOOXMLChartWriter.GetChartColorXML(AIndent: Integer;
var
indent: String;
rgbStr: String;
- alpha: Integer;
begin
- if (AColor.Transparency > 0) then
- begin
- alpha := round((1.0 - AColor.Transparency) * FACTOR_MULTIPLIER);
- rgbStr := Format('',
- [HtmlColorStr(AColor.Color), alpha], FPointSeparatorsettings
- );
- end else
- rgbstr := Format('',
- [ HtmlColorStr(AColor.Color) ]
- );
-
indent := DupeString(' ', AIndent);
-
+ rgbStr := GetChartColorXML(AColor);
Result :=
indent + '<' + ANodeName + '>' + LE +
indent + ' ' + rgbStr + LE +
indent + '' + ANodeName + '>';
end;
+function TsSpreadOOXMLChartWriter.GetChartColorXML(AColor: TsChartColor): String;
+var
+ alpha: Integer;
+begin
+ if (AColor.Transparency > 0) then
+ begin
+ alpha := round((1.0 - AColor.Transparency) * FACTOR_MULTIPLIER);
+ Result := Format('',
+ [HtmlColorStr(AColor.Color), alpha]
+ );
+ end else
+ Result := Format('',
+ [ HtmlColorStr(AColor.Color) ]
+ );
+end;
{@@ ----------------------------------------------------------------------------
Assembles the xml string for the children of a node (fill and line style)
@@ -3455,6 +3479,14 @@ const
var
indent: String;
hatch: TsChartHatch;
+ gradient: TsChartGradient;
+ step: TsChartGradientStep;
+ gSteps: String = '';
+ gStyle: String = '';
+ lStr: String = '';
+ tStr: String = '';
+ rStr: String = '';
+ bStr: String = '';
i: Integer;
presetIdx: Integer;
alpha: Integer;
@@ -3466,8 +3498,62 @@ begin
Result := indent + ''
else
case AFill.Style of
+ // Solid fills
cfsSolid:
Result := GetChartColorXML(AIndent + 2, 'a:solidFill', AFill.Color);
+
+ // Gradient fills
+ cfsGradient:
+ begin
+ gradient := AChart.Gradients[AFill.Gradient];
+ gSteps := indent + ' ' + LE;
+ for i := 0 to gradient.NumSteps - 1 do
+ begin
+ step := gradient.Steps[i];
+ gSteps := gSteps + Format(
+ indent + ' ' + LE +
+ indent + ' %s' + LE +
+ indent + ' ' + LE,
+ [ step.Value * FACTOR_MULTIPLIER, GetChartColorXML(step.Color) ]
+ );
+ end;
+ gSteps := gSteps + indent + ' ' + LE;
+ case gradient.Style of
+ cgsLinear:
+ gStyle := indent + Format(' ',
+ [ gradient.Angle * ANGLE_MULTIPLIER ]
+ );
+ cgsAxial,
+ cgsRadial,
+ cgsElliptic,
+ cgsSquare,
+ cgsRectangular,
+ cgsShape:
+ begin
+ case gradient.Style of
+ cgsRectangular, cgsAxial: gStyle := 'rect';
+ cgsElliptic, cgsRadial: gStyle := 'circle';
+ else gStyle := 'shape';
+ end;
+ if gradient.CenterX <> 0 then lStr := Format('l="%.0f" ', [gradient.CenterX * FACTOR_MULTIPLIER]);
+ if gradient.CenterX <> 1.0 then rStr := Format('r="%.0f" ', [(1.0-gradient.CenterX) * FACTOR_MULTIPLIER]);
+ if gradient.CenterY <> 0 then tStr := Format('t="%.0f" ', [gradient.CenterY * FACTOR_MULTIPLIER]);
+ if gradient.CenterY <> 1.0 then bStr := Format('b="%.0f" ', [(1.0-gradient.CenterY) * FACTOR_MULTIPLIER]);
+ gStyle := Format(
+ indent + ' ' + LE +
+ indent + ' ' + LE +
+ indent + ' ' + LE,
+ [ gStyle, lStr, tStr, rStr, bStr ]
+ );
+ end;
+ end;
+ Result := indent + '' + LE +
+ gSteps +
+ gStyle +
+ indent + '' + LE;
+ end;
+
+ // Hatched and pattern fills
cfsHatched, cfsSolidHatched:
begin
hatch := AChart.Hatches[AFill.Hatch];