program PlotToPng; // Atlas Plot quick start: build a chart in code, export it as PNG, SVG and CSV // without showing a window, then verify the files. {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes, System.IOUtils, Vcl.Graphics, Vcl.Imaging.pngimage, Atlas.Plot, Atlas.PlotSeries, Atlas.PlotAxis; const Months: array[0..11] of string = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); OrderCounts: array[0..11] of Double = (42, 38, 51, 47, 55, 61, 58, 64, 70, 66, 73, 81); Revenue: array[0..11] of Double = (12.5, 11.2, 15.8, 14.1, 17.3, 19.0, 18.2, 20.6, 22.9, 21.4, 24.0, 27.5); procedure Check(Condition: Boolean; const What: string); begin if not Condition then raise Exception.Create('Check failed: ' + What); Writeln(' ok: ', What); end; procedure BuildChart(Plot: TAtlasPlot); var Orders, Sales: TAtlasPlotSeries; I: Integer; begin Plot.BeginUpdate; try Plot.Title := 'Orders and revenue, 2026'; Plot.Footnote := 'Revenue in thousands'; Plot.LegendPosition := lpBottom; Plot.ArgumentAxis.Title := 'Month'; Plot.ValueAxis.Title := 'Count / thousands'; Plot.ValueAxis.Grid := True; Orders := Plot.AddSeries('Orders', pkBar); Sales := Plot.AddSeries('Revenue', pkLine, clMaroon); Sales.LineWidth := 3; Sales.ShowPoints := True; Sales.Marks := pmValue; for I := 0 to High(Months) do begin // AddY numbers the argument 0, 1, 2, ... and the text becomes a category tick. Orders.AddY(OrderCounts[I], Months[I]); Sales.AddY(Revenue[I]); end; // A least-squares fit of the revenue; a snapshot that does not follow later edits. Plot.AddTrendLine(Sales).LineWidth := 1; finally Plot.EndUpdate; end; end; procedure VerifyPng(const FileName: string); var Png: TPngImage; begin Check(TFile.GetSize(FileName) > 1000, 'PNG file has content'); Png := TPngImage.Create; try Png.LoadFromFile(FileName); Check((Png.Width = 800) and (Png.Height = 500), 'PNG is 800 x 500'); finally Png.Free; end; end; procedure VerifySvg(const FileName: string); var Svg: string; begin Svg := TFile.ReadAllText(FileName, TEncoding.UTF8); Check(Pos(' 0, 'SVG root has the requested size'); Check(Pos('>Orders and revenue, 2026<', Svg) > 0, 'SVG contains the title'); Check((Pos('>Jan<', Svg) > 0) and (Pos('>Dec<', Svg) > 0), 'SVG shows category ticks from the point text'); Check(Pos(' 0, 'SVG clips the series to the plot area'); end; procedure VerifyCsv(const FileName: string; Plot: TAtlasPlot); var Lines: TArray; Copy: TAtlasPlot; begin Lines := TFile.ReadAllLines(FileName, TEncoding.UTF8); Check(Lines[0] = 'series,kind,x,y,text', 'CSV header is series,kind,x,y,text'); // 12 order rows, 12 revenue rows and the two-point trend line. Check(Length(Lines) = 1 + 12 + 12 + 2, 'CSV has one row per point'); Check(Lines[1] = 'Orders,bar,0,42,Jan', 'first row is the first order bar'); Check(Lines[13] = 'Revenue,line,0,12.5,', 'numbers use a decimal point regardless of locale'); Check(Pos('Revenue trend,line,', Lines[25]) = 1, 'the trend line is exported as a series'); Copy := TAtlasPlot.Create(nil); try Copy.DataFromCSV(TFile.ReadAllText(FileName, TEncoding.UTF8)); Check(Copy.Series.Count = 3, 'CSV import recreates all three series'); Check(Copy.Series.Find('orders').Kind = pkBar, 'series kinds survive the round trip'); Check(Copy.DataToCSV = Plot.DataToCSV, 'import and export are symmetric'); finally Copy.Free; end; end; procedure Run; var Plot: TAtlasPlot; Folder: string; begin Folder := TPath.Combine(GetCurrentDir, 'plot-output'); TDirectory.CreateDirectory(Folder); Plot := TAtlasPlot.Create(nil); try BuildChart(Plot); Check(Plot.Series.Count = 3, 'chart has two data series and one trend line'); Check(Plot.Series[2].Count = 2, 'a trend line has exactly two points'); Writeln('Exporting to ', Folder); // The extension selects the format; width and height are in pixels. Plot.SaveToFile(TPath.Combine(Folder, 'orders.png'), 800, 500); Plot.SaveToFile(TPath.Combine(Folder, 'orders.svg'), 800, 500); Plot.SaveToFile(TPath.Combine(Folder, 'orders.csv')); VerifyPng(TPath.Combine(Folder, 'orders.png')); VerifySvg(TPath.Combine(Folder, 'orders.svg')); VerifyCsv(TPath.Combine(Folder, 'orders.csv'), Plot); // Invalid input raises EAtlasPlotError and leaves the chart unchanged. try Plot.Series[0].Add(12, 1E101); Check(False, 'a value beyond 1E100 must be rejected'); except on EAtlasPlotError do Check(Plot.Series[0].Count = 12, 'a value beyond 1E100 is rejected and nothing changes'); end; Check(Length(TDirectory.GetFiles(Folder, '*.part')) = 0, 'no partial files are left behind'); finally Plot.Free; end; end; begin try Run; Writeln('PlotToPng: all checks passed'); except on E: Exception do begin Writeln(E.ClassName, ': ', E.Message); ExitCode := 1; end; end; end.