YOUSEFUL / DELPHI / SAMPLE PROJECTS
Plot to PNG
A console program that creates a TAtlasPlot in code, adds a bar series and a
line series with a trend line, and exports the chart as PNG, SVG and CSV without
ever showing a window. It then verifies the files it wrote and exits with code 0.
What it demonstrates
AddSeries,AddYwith category text,Marks,ShowPoints,LineWidthTitle,Footnote,LegendPosition, axis titlesAddTrendLine(a least-squares snapshot)SaveToFilechoosing PNG, SVG or CSV by extension, at a given pixel sizeDataFromCSV/DataToCSVround tripEAtlasPlotErroron invalid input, leaving the chart unchangedBeginUpdate/EndUpdatearound bulk changes
Build and run
Open PlotToPng.dpr in Delphi 13; the IDE creates the project file. Add the Atlas Plot src folder to the project's search path, then build and run for Win32 or Win64. No packages need to be installed. The program needs no input. It checks its own results and stops with an error if any of them differs from the output below.
The program writes plot-output/orders.png, orders.svg and orders.csv in its current directory.
Expected output
ok: chart has two data series and one trend line
ok: a trend line has exactly two points
Exporting to ...\plot-output
ok: PNG file has content
ok: PNG is 800 x 500
ok: SVG root has the requested size
ok: SVG contains the title
ok: SVG shows category ticks from the point text
ok: SVG clips the series to the plot area
ok: CSV header is series,kind,x,y,text
ok: CSV has one row per point
ok: first row is the first order bar
ok: numbers use a decimal point regardless of locale
ok: the trend line is exported as a series
ok: CSV import recreates all three series
ok: series kinds survive the round trip
ok: import and export are symmetric
ok: a value beyond 1E100 is rejected and nothing changes
ok: no partial files are left behind
PlotToPng: all checks passedAdapting it
Replace the Months, OrderCounts and Revenue constants with your own data,
or load a CSV file with Plot.DataFromCSV(TFile.ReadAllText(...)) instead of
calling AddY. Change the extension in SaveToFile to .bmp or .emf for the
other image formats, or call RenderTo(Printer.Canvas, ...) to print.
Project files
The ZIP contains every sample project with its README. See getting started for the search path to add.
Source
PlotToPng.dpr
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('<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500"', Svg) > 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('<clipPath', Svg) > 0, 'SVG clips the series to the plot area');
end;
procedure VerifyCsv(const FileName: string; Plot: TAtlasPlot);
var
Lines: TArray<string>;
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.