From 162a4049ff3297912cc59825f5f871edb966b203 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 4 Jan 2024 23:02:45 +0000 Subject: [PATCH] fpspreadsheet: More reading of xlsx charts. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9120 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../fpspreadsheet/source/common/fpschart.pas | 14 +- .../source/common/fpsopendocument.pas | 4 +- .../source/common/fpsopendocumentchart.pas | 10 +- .../source/common/xlsxooxmlchart.pas | 409 +++++++++++++++--- 4 files changed, 375 insertions(+), 62 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpschart.pas b/components/fpspreadsheet/source/common/fpschart.pas index 1209c88b2..370cb9298 100644 --- a/components/fpspreadsheet/source/common/fpschart.pas +++ b/components/fpspreadsheet/source/common/fpschart.pas @@ -225,7 +225,7 @@ type TsChartText = class(TsChartFillElement) private FCaption: String; - FRotationAngle: Integer; + FRotationAngle: single; FFont: TsFont; FPosX, FPosY: Double; public @@ -234,7 +234,7 @@ type procedure CopyFrom(ASource: TsChartElement); override; property Caption: String read FCaption write FCaption; property Font: TsFont read FFont write FFont; - property RotationAngle: Integer read FRotationAngle write FRotationAngle; + property RotationAngle: single read FRotationAngle write FRotationAngle; property PosX: Double read FPosX write FPosX; property PosY: Double read FPosY write FPosY; property Visible; @@ -251,6 +251,7 @@ type FAutomaticMax: Boolean; FAutomaticMin: Boolean; FAutomaticMajorInterval: Boolean; + FAutomaticMinorInterval: Boolean; FAutomaticMinorSteps: Boolean; FAxisLine: TsChartLine; FCategoryRange: TsChartRange; @@ -262,13 +263,14 @@ type FLabelFormatFromSource: Boolean; FLabelFormatDateTime: String; FLabelFormatPercent: String; - FLabelRotation: Integer; + FLabelRotation: Single; FLogarithmic: Boolean; FMajorInterval: Double; FMajorTicks: TsChartAxisTicks; FMax: Double; FMin: Double; FMinorCount: Integer; + FMinorInterval: Double; FMinorTicks: TsChartAxisTicks; FPosition: TsChartAxisPosition; FTitle: TsChartText; @@ -286,6 +288,7 @@ type property AutomaticMax: Boolean read FAutomaticMax write FAutomaticMax; property AutomaticMin: Boolean read FAutomaticMin write FAutomaticMin; property AutomaticMajorInterval: Boolean read FAutomaticMajorInterval write FAutomaticMajorInterval; + property AutomaticMinorInterval: Boolean read FAutomaticMinorInterval write FAutomaticMinorInterval; property AutomaticMinorSteps: Boolean read FAutomaticMinorSteps write FAutomaticMinorSteps; property AxisLine: TsChartLine read FAxisLine write FAxisLine; property CategoryRange: TsChartRange read FCategoryRange write FCategoryRange; @@ -297,7 +300,7 @@ type property LabelFormatDateTime: String read FLabelFormatDateTime write FLabelFormatDateTime; property LabelFormatFromSource: Boolean read FLabelFormatFromSource write FLabelFormatFromSource; property LabelFormatPercent: String read FLabelFormatPercent write FLabelFormatPercent; - property LabelRotation: Integer read FLabelRotation write FLabelRotation; + property LabelRotation: Single read FLabelRotation write FLabelRotation; property Logarithmic: Boolean read FLogarithmic write FLogarithmic; property MajorGridLines: TsChartLine read FMajorGridLines write FMajorGridLines; property MajorInterval: Double read FMajorInterval write FMajorInterval; @@ -306,6 +309,7 @@ type property Min: Double read FMin write FMin; property MinorGridLines: TsChartLine read FMinorGridLines write FMinorGridLines; property MinorCount: Integer read FMinorCount write FMinorCount; + property MinorInterval: Double read FMinorInterval write FMinorInterval; property MinorTicks: TsChartAxisTicks read FMinorTicks write FMinorTicks; property Position: TsChartAxisPosition read FPosition write FPosition; property PositionValue: Double read FPositionValue write FPositionValue; @@ -1419,6 +1423,7 @@ begin FAutomaticMax := TsChartAxis(ASource).AutomaticMax; FAutomaticMin := TsChartAxis(ASource).AutomaticMin; FAutomaticMajorInterval := TsChartAxis(ASource).AutomaticMajorInterval; + FAutomaticMinorInterval := TsChartAxis(ASource).AutomaticMinorInterval; FAutomaticMinorSteps := TsChartAxis(ASource).AutomaticMinorSteps; FAxisLine.CopyFrom(TsChartAxis(ASource).AxisLine); FCategoryRange.CopyFrom(TsChartAxis(ASource).CategoryRange); @@ -1438,6 +1443,7 @@ begin FMax := TsChartAxis(ASource).Max; FMin := TsChartAxis(ASource).Min; FMinorCount := TsChartAxis(ASource).MinorCount; + FMinorInterval := TsChartAxis(ASource).MinorInterval; FMinorTicks := TsChartAxis(ASource).MinorTicks; FPosition := TsChartAxis(ASource).Position; FTitle.CopyFrom(TsChartAxis(ASource).Title); diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index 1d0d5e154..ac644780c 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -9082,8 +9082,8 @@ begin r1 := chart.Row; c1 := chart.Col; - rOffs1 := chart.OffsetX; - cOffs1 := chart.OffsetY; + rOffs1 := chart.OffsetY; + cOffs1 := chart.OffsetX; w := chart.Width; h := chart.Height; sheet.CalcDrawingExtent(true, w, h, r1, c1, r2, c2, rOffs1, cOffs1, rOffs2, cOffs2, x, y); diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas index da6299a98..d16cc7c82 100644 --- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas +++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas @@ -307,7 +307,7 @@ type constructor TsChartNumberFormatList.Create; begin inherited; - Add(''); // default number format + Add('N0'); // default number format end; // Adds a new format, but make sure to avoid duplicates. @@ -390,6 +390,7 @@ begin FChartFiles := TStringList.Create; FNumberFormatList := TsChartNumberFormatList.Create; FNumberFormatList.NameValueSeparator := ':'; + FNumberFormatList.Add('N0'); FStreamList := TStreamList.Create; FPieSeriesStartAngle := 999; @@ -2097,7 +2098,7 @@ function TsSpreadOpenDocChartWriter.GetChartAxisStyleAsXML( var chart: TsChart; indent: String; - angle: Integer; + angle: single; textProps: String = ''; graphProps: String = ''; chartProps: String = ''; @@ -2114,6 +2115,8 @@ begin numStyle := GetNumberFormatID(Axis.LabelFormatPercent) else numStyle := GetNumberFormatID(Axis.LabelFormat); + if numStyle <> 'N0' then + chartProps := chartProps + 'chart:link-data-style-to-source="false" '; // Show axis labels if Axis.ShowLabels then @@ -2224,7 +2227,7 @@ var axis: TsChartAxis; font: TsFont; indent: String; - rotAngle: Integer; + rotAngle: Single; chartProps: String = ''; textProps: String = ''; begin @@ -2811,6 +2814,7 @@ var regression: TsChartRegression; begin FNumberFormatList.Clear; + FNumberFormatList.Add('N0'); // Formats of axis labels FNumberFormatList.Add(AChart.XAxis.LabelFormat); diff --git a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas index ccce97ea6..f355df558 100644 --- a/components/fpspreadsheet/source/common/xlsxooxmlchart.pas +++ b/components/fpspreadsheet/source/common/xlsxooxmlchart.pas @@ -25,19 +25,22 @@ type function ReadChartColor(ANode: TDOMNode; ADefault: TsColor): TsColor; procedure ReadChartFillAndLineProps(ANode: TDOMNode; AChart: TsChart; AFill: TsChartFill; ALine: TsChartLine); + procedure ReadChartFontProps(ANode: TDOMNode; AFont: TsFont); procedure ReadChartLineProps(ANode: TDOMNode; AChart: TsChart; AChartLine: TsChartLine); + procedure ReadChartTextProps(ANode: TDOMNode; AFont: TsFont; var AFontRotation: Single); protected procedure ReadChart(ANode: TDOMNode; AChart: TsChart); procedure ReadChartAxis(ANode: TDOMNode; AChart: TsChart; AChartAxis: TsChartAxis); + procedure ReadChartAxisScaling(ANode: TDOMNode; AChartAxis: TsChartAxis); function ReadChartAxisTickMarks(ANode: TDOMNode): TsChartAxisTicks; procedure ReadChartBarSeries(ANode: TDOMNode; AChart: TsChart); procedure ReadChartLegend(ANode: TDOMNode; AChartLegend: TsChartLegend); procedure ReadChartPlotArea(ANode: TDOMNode; AChart: TsChart); + procedure ReadChartSeriesLabels(ANode: TDOMNode; ASeries: TsChartSeries); procedure ReadChartSeriesProps(ANode: TDOMNode; ASeries: TsChartSeries); procedure ReadChartSeriesRange(ANode: TDOMNode; ARange: TsChartRange); procedure ReadChartSeriesTitle(ANode: TDOMNode; ASeries: TsChartSeries); - procedure ReadChartText(ANode: TDOMNode; AText: TsChartText); - procedure ReadChartTitle(ANode: TDOMNode; AChartTitle: TsChartText); + procedure ReadChartTitle(ANode: TDOMNode; ATitle: TsChartText); public constructor Create(AReader: TsBasicSpreadReader); override; @@ -69,6 +72,7 @@ uses const PTS_MULTIPLIER = 12700; + ANGLE_MULTIPLIER = 60000; { TsSpreadOOXMLChartReader } @@ -125,11 +129,16 @@ procedure TsSpreadOOXMLChartReader.ReadChartAxis(ANode: TDOMNode; var nodeName, s: String; n: Integer; + x: Single; node: TDOMNode; begin if ANode = nil then exit; + // Defaults + AChartAxis.Title.Caption := ''; + AChartAxis.LabelRotation := 0; + while Assigned(ANode) do begin nodeName := ANode.NodeName; @@ -140,6 +149,10 @@ begin if (s <> '') and TryStrToInt(s, n) then AChartAxis.ID := n; end; + 'c:axPos': + ; + 'c:scaling': + ReadChartAxisScaling(ANode.FirstChild, AChartAxis); 'c:majorGridlines': begin node := ANode.FindNode('c:spPr'); @@ -153,7 +166,7 @@ begin ReadChartLineProps(node.FirstChild, AChart, AChartAxis.MinorGridLines); end; 'c:title': - ReadChartText(ANode.FindNode('c:tx'), AChartAxis.Title); + ReadChartTitle(ANode.FirstChild, AChartAxis.Title); 'c:numFmt': ; 'c:majorTickMark': @@ -164,6 +177,72 @@ begin ; 'c:spPr': ReadChartLineProps(ANode.FirstChild, AChart, AChartAxis.AxisLine); + 'c:majorUnit': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and TryStrToFloat(s, x, FPointSeparatorSettings) then + begin + AChartAxis.AutomaticMajorInterval := false; + AChartAxis.MajorInterval := x; + end; + end; + 'c:minorUnit': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and TryStrToFloat(s, x, FPointSeparatorSettings) then + begin + AChartAxis.AutomaticMinorInterval := false; + AChartAxis.MinorInterval := x; + end; + end; + 'c:txPr': // Axis labels + begin + x := 0; + ReadChartTextProps(ANode, AChartAxis.LabelFont, x); + AChartAxis.LabelRotation := x; + end; + end; + ANode := ANode.NextSibling; + end; +end; + +procedure TsSpreadOOXMLChartReader.ReadChartAxisScaling(ANode: TDOMNode; + AChartAxis: TsChartAxis); +var + nodeName, s: String; + node: TDOMNode; + x: Double; +begin + if ANode = nil then + exit; + + while Assigned(ANode) do + begin + nodeName := ANode.NodeName; + case nodeName of + 'c:orientation': + begin + s := GetAttrValue(ANode, 'val'); + AChartAxis.Inverted := (s = 'maxMin'); + end; + 'c:max': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and TryStrToFloat(s, x, FPointSeparatorSettings) then + begin + AChartAxis.AutomaticMax := false; + AChartAxis.Max := x; + end; + end; + 'c:min': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and TryStrToFloat(s, x, FPointSeparatorSettings) then + begin + AChartAxis.AutomaticMin := false; + AChartAxis.Min := x; + end; + end; end; ANode := ANode.NextSibling; end; @@ -315,17 +394,99 @@ begin end; 'a:ln': ReadChartLineProps(ANode, AChart, ALine); + 'a:effectLst': + ; end; ANode := ANode.NextSibling; end; end; +{@@ ---------------------------------------------------------------------------- + Extracts the properties of a font + @param ANode This is a "a:defRPr" node + @param AFont Font to which the parameters are applied +-------------------------------------------------------------------------------} +procedure TsSpreadOOXMLChartReader.ReadChartFontProps(ANode: TDOMNode; + AFont: TsFont); +{ Example: + + + + + + + + } +var + node: TDOMNode; + nodeName, s: String; + x: Double; + n: Integer; +begin + if ANode = nil then + exit; + + nodeName := ANode.NodeName; + if not ((nodeName = 'a:defRPr') or (nodeName = 'a:rPr')) then + exit; + + // Font size + s := GetAttrValue(ANode, 'sz'); + if (s <> '') and TryStrToInt(s, n) then + AFont.Size := n/100; + + // Font styles + if GetAttrValue(ANode, 'b') = '1' then + AFont.Style := AFont.Style + [fssBold]; + + if GetAttrValue(ANode, 'i') = '1' then + AFont.Style := AFont.Style + [fssItalic]; + + if GetAttrValue(ANode, 'u') = '1' then + AFont.Style := AFont.Style + [fssUnderline]; + + s := GetAttrValue(ANode, 'strike'); + if (s <> '') and (s <> 'noStrike') then + AFont.Style := AFont.Style + [fssStrikeOut]; + + node := ANode.FirstChild; + while Assigned(node) do + begin + nodeName := node.NodeName; + case nodeName of + // Font color + 'a:solidFill': + AFont.Color := ReadChartColor(node.FirstChild, scBlack); + + // font name + 'a:latin': + begin + s := GetAttrValue(node, 'typeface'); + if s <> '' then + AFont.FontName := s; + end; + + // not supported + 'a:ea': ; + 'a:cs': ; + end; + node := node.NextSibling; + end; +end; + +{@@ ---------------------------------------------------------------------------- + Extracts the legend properties + + @param ANode This is the "c:legend" node + @param AChartLegend Legend to which the values are applied +-------------------------------------------------------------------------------} procedure TsSpreadOOXMLChartReader.ReadChartLegend(ANode: TDOMNode; AChartLegend: TsChartLegend); var - child: TDOMNode; nodeName, s: String; + dummy: Single; begin if ANode = nil then exit; @@ -334,6 +495,7 @@ begin begin nodeName := ANode.NodeName; case nodeName of + // Legend position with respect to the plot area 'c:legendPos': begin s := GetAttrValue(ANode, 'val'); @@ -344,16 +506,31 @@ begin 'r': AChartLegend.Position := lpRight; end; end; + + // Formatting of individual legend items, not supported 'c:legendEntry': ; + + // Overlap with plot area 'c:overlay': begin s := GetAttrValue(ANode, 'val'); AChartLegend.canOverlapPlotArea := (s = '1'); end; + + // Background and border 'c:spPr': ReadChartFillAndLineProps(ANode.FirstChild, AChartLegend.Chart, AChartLegend.Background, AChartLegend.Border); + + // Legend font + 'c:txPr': + begin + dummy := 0; + ReadChartTextProps(ANode, AChartLegend.Font, dummy); + //AChartLegend.RotationAngle := dummy; // we do not support rotated text in legend + end; end; + ANode := ANode.NextSibling; end; end; @@ -374,7 +551,6 @@ var child, child2: TDOMNode; nodeName, s: String; w, d, sp: Int64; - n: Integer; noLine: Boolean; begin if ANode = nil then @@ -422,7 +598,7 @@ begin s := GetAttrValue(child2, 'd'); if TryStrToInt64(s, d) then begin - s := getAttrValue(child2, 'sp'); + s := GetAttrValue(child2, 'sp'); if TryStrToInt64(s, sp) then AChartLine.Style := AChart.LineStyles.Add('', d, 1, (d+sp), 0, 0, false); end; @@ -460,13 +636,87 @@ begin end; end; +procedure TsSpreadOOXMLChartReader.ReadChartSeriesLabels(ANode: TDOMNode; + ASeries: TsChartSeries); +var + nodeName, s: String; + child, child2, child3: TDOMNode; +begin + if ANode = nil then + exit; + while Assigned(ANode) do + begin + nodeName := ANode.NodeName; + case nodeName of + 'c:spPr': + ReadChartFillAndLineProps(ANode.FirstChild, ASeries.Chart, ASeries.LabelBackground, ASeries.LabelBorder); + 'c:txPr': + begin + child := ANode.FindNode('a:p'); + if Assigned(child) then + begin + child2 := child.FirstChild; + while Assigned(child2) do + begin + nodeName := child2.NodeName; + if nodeName = 'a:pPr' then + begin + child3 := child2.FindNode('a:defRPr'); + if Assigned(child3) then + ReadChartFontProps(child3, ASeries.LabelFont); + end; + child2 := child2.NextSibling; + end; + end; + end; + 'c:showLegendKey': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and (s <> '0') then + ASeries.DataLabels := ASeries.DataLabels + [cdlSymbol]; + end; + 'c:showVal': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and (s <> '0') then + ASeries.DataLabels := ASeries.DataLabels + [cdlValue]; + end; + 'c:showCatName': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and (s <> '0') then + ASeries.DataLabels := ASeries.DataLabels + [cdlCategory]; + end; + 'c:showSerName': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and (s <> '0') then + ASeries.DataLabels := ASeries.DataLabels + [cdlSeriesName]; + end; + 'c:showPercent': + begin + s := GetAttrValue(ANode, 'val'); + if (s <> '') and (s <> '0') then + ASeries.DataLabels := ASeries.DataLabels + [cdlPercentage]; + end; + 'c:showBubbleSize': + ; + 'c:showLeaderLines': + ; + 'c:extLst': + ; + end; + ANode := ANode.NextSibling; + end; +end; + procedure TsSpreadOOXMLChartReader.ReadChartSeriesProps(ANode: TDOMNode; ASeries: TsChartSeries); var nodeName: String; begin if ANode = nil then exit; - while ANode <> nil do + while Assigned(ANode) do begin nodeName := ANode.NodeName; case nodeName of @@ -475,11 +725,20 @@ begin 'c:tx': ReadChartSeriesTitle(ANode.FirstChild, ASeries); 'c:cat': - ReadChartSeriesRange(ANode.FirstChild, ASeries.XRange); + if ASeries.ChartType = ctScatter then + ReadChartSeriesRange(ANode.FirstChild, ASeries.XRange) + else + ReadChartSeriesRange(ANode.FirstChild, ASeries.LabelRange); 'c:val': ReadChartSeriesRange(ANode.FirstChild, ASeries.YRange); 'c:spPr': ReadChartFillAndLineProps(ANode.FirstChild, ASeries.Chart, ASeries.Fill, ASeries.Line); + 'c:dLbls': + ReadChartSeriesLabels(ANode.Firstchild, ASeries); + 'c:invertIfNegative': + ; + 'c:extLst': + ; end; ANode := ANode.NextSibling; end; @@ -540,11 +799,12 @@ begin end; end; -procedure TsSpreadOOXMLChartReader.ReadChartTitle(ANode: TDOMNode; AChartTitle: TsChartText); +{ Extracts the chart and axis titles, their formatting and their texts. } +procedure TsSpreadOOXMLChartReader.ReadChartTitle(ANode: TDOMNode; ATitle: TsChartText); var - nodeName: String; + nodeName, s, totalText: String; child, child2, child3, child4: TDOMNode; - s: String; + n: Integer; begin if ANode = nil then exit; @@ -554,58 +814,101 @@ begin case nodeName of 'c:tx': begin - child := ANode.Firstchild; - while Assigned(child) do + child := ANode.FindNode('c:rich'); + if Assigned(child) then begin - nodeName := child.NodeName; - case nodeName of - 'c:rich': - begin - child2 := child.FirstChild; - while Assigned(child2) do + child2 := child.FirstChild; + while Assigned(child2) do + begin + nodeName := child2.NodeName; + case nodeName of + 'a:bodyPr': begin - nodeName := child2.NodeName; - case nodeName of - 'a:p': - begin - child3 := child2.FirstChild; - while Assigned(child3) do - begin - nodeName := child3.NodeName; - case nodeName of - 'a:r': - begin - child4 := child3.FirstChild; - while Assigned(child4) do - begin - nodeName := child4.NodeName; - case nodeName of - 'a:t': - AChartTitle.Caption := GetNodeValue(child4); - end; - child4 := child4.NextSibling; - end; - end; - end; - child3 := child3.NextSibling; - end; - end; - end; - child2 := child2.NextSibling; + s := GetAttrValue(ANode, 'rot'); + if (s <> '') and TryStrToInt(s, n) then + ATitle.RotationAngle := -n / ANGLE_MULTIPLIER; end; - end; + 'a:lstStyle': + ; + 'a:p': + begin + totalText := ''; + child3 := child2.FirstChild; + while Assigned(child3) do + begin + nodeName := child3.NodeName; + case NodeName of + 'a:pPr': + begin + child4 := child3.FindNode('a:defRPr'); + ReadChartFontProps(child4, ATitle.Font); + end; + 'a:r': + begin + child4 := child3.FindNode('a:t'); + totalText := totalText + GetNodeValue(child4); + end; + end; + child3 := child3.NextSibling; + end; + ATitle.Caption := totalText; + end; + end; + child2 := child2.NextSibling; end; - child := child.NextSibling; - end; - end; + end; // "rich" node + end; // "tx" node + 'c:overlay': + ; + 'c:spPr': + ReadChartFillAndLineProps(ANode.FirstChild, ATitle.Chart, ATitle.Background, ATitle.Border); + 'c:txPr': + ; end; ANode := ANode.NextSibling; end; end; -procedure TsSpreadOOXMLChartReader.ReadChartText(ANode: TDOMNode; - AText: TsChartText); +{ ANode is a "c:txPr" node } +procedure TsSpreadOOXMLChartReader.ReadChartTextProps(ANode: TDOMNode; AFont: TsFont; + var AFontRotation: Single); +var + nodeName, s: String; + n: Integer; + child1, child2: TDOMNode; begin + if (ANode = nil) or (ANode.NodeName <> 'c:txPr') then exit; + + ANode := ANode.FirstChild; + while Assigned(ANode) do + begin + nodeName := ANode.NodeName; + case nodeName of + 'a:bodyPr': + { } + begin + s := GetAttrValue(ANode, 'rot'); + if (s <> '') and TryStrToInt(s, n) then + AFontRotation := -n / ANGLE_MULTIPLIER; + end; + 'a:lstStyle': + ; + 'a:p': + begin + child1 := ANode.FirstChild; + if Assigned(child1) then + begin + child2 := child1.FindNode('a:defRPr'); + if Assigned(child2) then + ReadChartFontProps(child2, AFont); + end; + end; + 'a:endParaRPr': + ; + end; + ANode := ANode.NextSibling; + end; end; procedure TsSpreadOOXMLChartReader.ReadChartXML(AStream: TStream; AChart: TsChart;