feat: add RenderSingleSeriesToPng for standalone underlying chart

This commit is contained in:
2026-07-21 10:37:18 +02:00
parent a75d1711e6
commit 3887404572

View File

@@ -187,6 +187,130 @@ public static class SkiaChartRendererV2
return imgData.ToArray(); return imgData.ToArray();
} }
// ═══════════════════════════════════════════════════════════════════
// Entry point — grafico standalone singolo sottostante (no certificato)
// ═══════════════════════════════════════════════════════════════════
public static byte[] RenderSingleSeriesToPng(UnderlyingChartData data, int width = 1100, int height = 700, bool jpeg = false)
{
using var surface = SKSurface.Create(new SKImageInfo(width, height));
var canvas = surface.Canvas;
canvas.Clear(SKColors.White);
float titleBottom = DrawUnderlyingTitle(canvas, width, data.Nome);
float marginLeft = 70;
float marginRight = 40;
float marginTop = titleBottom + 10;
float marginBottom = 45;
var plotArea = new SKRect(marginLeft, marginTop, width - marginRight, height - marginBottom);
var points = data.Points.OrderBy(p => p.Date).ToList();
if (points.Count == 0)
return Array.Empty<byte>();
DateTime minDate = points[0].Date;
DateTime maxDate = points[^1].Date;
double minY = (double)points.Min(p => p.Px);
double maxY = (double)points.Max(p => p.Px);
double range = maxY - minY;
if (range == 0) range = Math.Max(minY * 0.1, 1);
double margin = range * 0.1;
minY -= margin;
maxY += margin;
DrawGrid(canvas, plotArea, minDate, maxDate, minY, maxY);
DrawUnderlyingAxisLabels(canvas, plotArea, minDate, maxDate, minY, maxY);
DrawUnderlyingSeries(canvas, plotArea, points, minDate, maxDate, minY, maxY);
using var borderPaint = new SKPaint
{
Color = SKColors.Gray, StrokeWidth = 1,
Style = SKPaintStyle.Stroke, IsAntialias = true,
};
canvas.DrawRect(plotArea, borderPaint);
using var image = surface.Snapshot();
using var imgData = jpeg
? image.Encode(SKEncodedImageFormat.Jpeg, 90)
: image.Encode(SKEncodedImageFormat.Png, 95);
return imgData.ToArray();
}
private static float DrawUnderlyingTitle(SKCanvas canvas, int width, string nome)
{
float y = 15f;
var boldFont = CreateFont(13f, bold: true);
using var titlePaint = new SKPaint { Color = TitleColor, IsAntialias = true };
canvas.DrawText(nome, width / 2f, y + 13, SKTextAlign.Center, boldFont, titlePaint);
return y + 22 + 5;
}
private static void DrawUnderlyingAxisLabels(SKCanvas canvas, SKRect area,
DateTime minDate, DateTime maxDate, double minY, double maxY)
{
var font = CreateFont(11);
using var paint = new SKPaint { Color = SKColors.DimGray, IsAntialias = true };
int ySteps = 8;
for (int i = 0; i <= ySteps; i++)
{
double val = maxY - ((maxY - minY) / ySteps) * i;
float y = area.Top + (area.Height / ySteps) * i;
canvas.DrawText($"{val:F2}", area.Left - 55, y + 4, SKTextAlign.Left, font, paint);
}
double totalDays = (maxDate - minDate).TotalDays;
int intervalMonths = XAxisIntervalMonths(minDate, maxDate);
string fmt = totalDays > 365 ? "MMM yy" : "dd/MM/yy";
if (intervalMonths > 0)
{
var d = XAxisStart(minDate, intervalMonths);
while (d <= maxDate)
{
float x = DateToX(d, area, minDate, maxDate);
if (x >= area.Left && x <= area.Right)
canvas.DrawText(d.ToString(fmt), x, area.Bottom + 20, SKTextAlign.Center, font, paint);
d = d.AddMonths(intervalMonths);
}
}
else
{
canvas.DrawText(minDate.ToString("dd/MM/yy"), area.Left, area.Bottom + 20, SKTextAlign.Left, font, paint);
canvas.DrawText(maxDate.ToString("dd/MM/yy"), area.Right, area.Bottom + 20, SKTextAlign.Right, font, paint);
}
}
private static void DrawUnderlyingSeries(SKCanvas canvas, SKRect area,
List<UnderlyingChartPoint> points,
DateTime minDate, DateTime maxDate, double minY, double maxY)
{
using var paint = new SKPaint
{
Color = CertColor, StrokeWidth = 2f,
Style = SKPaintStyle.Stroke, IsAntialias = true,
StrokeCap = SKStrokeCap.Round, StrokeJoin = SKStrokeJoin.Round,
};
using var path = new SKPath();
bool first = true;
foreach (var pt in points)
{
float x = DateToX(pt.Date, area, minDate, maxDate);
float y = ValueToY((double)pt.Px, area, minY, maxY);
y = Math.Max(area.Top, Math.Min(area.Bottom, y));
if (first) { path.MoveTo(x, y); first = false; }
else path.LineTo(x, y);
}
canvas.Save();
canvas.ClipRect(area);
canvas.DrawPath(path, paint);
canvas.Restore();
}
// ═══════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════
// Titolo // Titolo
// ═══════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════