feat: add FundAnagraficaRenderer (layout C: title strip + 3 columns)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 17:41:48 +02:00
parent d3c5395c58
commit 8e6341d980

View File

@@ -0,0 +1,266 @@
using CertReports.Syncfusion.Helpers;
using CertReports.Syncfusion.Models;
using Syncfusion.Drawing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
namespace CertReports.Syncfusion.Services.Implementations;
public class FundAnagraficaRenderer
{
private const float PageW = 595f - 2 * PdfTheme.PageMargin;
private const float PageH = 842f - 2 * PdfTheme.PageMargin - PdfTheme.FooterHeight;
private const float ColGap = 8f;
private const float Col1W = 190f;
private const float Col2W = 125f;
private const float Col3W = PageW - Col1W - Col2W - 2 * ColGap;
public PdfDocument Render(FundReportData data)
{
var doc = new PdfDocument();
var page = PdfTheme.AddA4Page(doc);
var g = page.Graphics;
var info = data.Info;
float y = 0f;
y = DrawTitle(g, info, y);
y += 6f;
y = DrawStrip(g, info, y);
y += 8f;
float col1X = 0f;
float col2X = Col1W + ColGap;
float col3X = col2X + Col2W + ColGap;
DrawAnagrafici(g, info, col1X, Col1W, y);
DrawEsg(g, info, col2X, Col2W, y);
DrawPerfGrid(g, info, col3X, Col3W, y);
PdfTheme.DrawFooter(g, PageW, PageH, 1, data.ShowBranding);
return doc;
}
private float DrawTitle(PdfGraphics g, FundInfo info, float y)
{
const float h = 22f;
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(0, y, PageW, h));
var fmt = new PdfStringFormat
{
Alignment = PdfTextAlignment.Center,
LineAlignment = PdfVerticalAlignment.Middle
};
var title = $"{info.Tipo ?? "Fondo"} — {info.Strumento} — {info.Isin}";
g.DrawString(title, PdfTheme.Bold,
new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 255, 255, 255))),
new RectangleF(0, y, PageW, h), fmt);
return y + h;
}
private float DrawStrip(PdfGraphics g, FundInfo info, float y)
{
const float h = 36f;
const float w1 = 100f;
const float w2 = 225f;
const float w3 = 150f;
const float gap = 8f;
DrawStripBox(g, "RATING / RANK",
info.Rank.HasValue ? info.Rank.Value.ToString("F2") : "—",
0f, y, w1, h);
DrawStripBox(g, "PREZZO",
info.Prezzo.HasValue ? $"{info.Prezzo.Value:F2} {info.Valuta}" : "—",
w1 + gap, y, w2, h);
DrawStripBox(g, "AGGIORNATO AL",
info.DataPrezzo.HasValue ? info.DataPrezzo.Value.ToString("dd/MM/yyyy") : "—",
w1 + gap + w2 + gap, y, w3, h);
return y + h;
}
private void DrawStripBox(PdfGraphics g, string label, string value,
float x, float y, float w, float h)
{
var bgBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 232, 238, 248)));
var borderPen = new PdfPen(new PdfColor(PdfTheme.AccentBlue), 0.5f);
g.DrawRectangle(bgBrush, new RectangleF(x, y, w, h));
g.DrawRectangle(borderPen, new RectangleF(x, y, w, h));
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(x, y, 3f, h));
var grayBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 136, 136, 136)));
var leftFmt = new PdfStringFormat { Alignment = PdfTextAlignment.Left };
g.DrawString(label, PdfTheme.Small, grayBrush,
new RectangleF(x + 6f, y + 2f, w - 8f, 10f), leftFmt);
g.DrawString(value, PdfTheme.Bold, PdfTheme.AccentBlueBrush,
new RectangleF(x + 6f, y + 13f, w - 8f, h - 15f), leftFmt);
}
private void DrawAnagrafici(PdfGraphics g, FundInfo info, float x, float w, float y)
{
y = DrawColHeader(g, "Dati Anagrafici", x, w, y);
var items = new (string Label, string Value)[]
{
("Società", info.Societa ?? "—"),
("Categoria MS", info.CategoriaMorningstar ?? "—"),
("Tipo", info.Tipo ?? "—"),
("Valuta", info.Valuta ?? "—"),
("Hedged", string.IsNullOrEmpty(info.Hedged) ? "—" : info.Hedged),
("Benchmark", info.Benchmark ?? "—"),
("Spese correnti", info.SpeseCorrenti.HasValue ? $"{info.SpeseCorrenti.Value:F2}%" : "—"),
("Catalogo", info.Catalogo ?? "—"),
("Proventi", info.Proventi ?? "—"),
("Data lancio", info.DataLancio.HasValue ? info.DataLancio.Value.ToString("dd/MM/yyyy") : "—"),
("Patrimonio", info.Patrimonio.HasValue ? $"{info.Patrimonio.Value:N0} EUR" : "—"),
};
DrawKvList(g, items, x, w, y);
}
private void DrawEsg(PdfGraphics g, FundInfo info, float x, float w, float y)
{
y = DrawColHeader(g, "ESG Score", x, w, y);
var scores = new (string Label, decimal? Value)[]
{
("Sustainability", info.Sustainability),
("Environmental", info.Environmental),
("Social", info.Social),
("Governance", info.Governance),
};
var greenBg = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 232, 245, 233)));
var greenPen = new PdfPen(new PdfColor(Color.FromArgb(255, 200, 230, 201)), 0.5f);
var grayLbl = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 85, 85, 85)));
var greenVal = new PdfSolidBrush(new PdfColor(PdfTheme.PositiveGreen));
foreach (var (label, val) in scores)
{
const float cardH = 36f;
g.DrawRectangle(greenBg, new RectangleF(x, y, w, cardH));
g.DrawRectangle(greenPen, new RectangleF(x, y, w, cardH));
g.DrawString(label, PdfTheme.Small, grayLbl,
new RectangleF(x + 5f, y + 2f, w - 8f, 12f));
var valText = (val.HasValue && val.Value != 0m) ? val.Value.ToString("F2") : "—";
g.DrawString(valText, PdfTheme.Bold, greenVal,
new RectangleF(x + 5f, y + 14f, w - 8f, 20f));
y += cardH + 3f;
}
}
private void DrawPerfGrid(PdfGraphics g, FundInfo info, float x, float w, float y)
{
y = DrawGridHeader(g, "PERFORMANCE · VOLATILITÀ · REND/RISK", x, w, y);
var periods = new (string Label, decimal? Perf, decimal? Vol, decimal? Rr)[]
{
("3 Mesi", info.P3M, info.V3M, info.R3M),
("6 Mesi", info.P6M, info.V6M, info.R6M),
("Da inizio anno", info.PYD, info.VYD, info.RYD),
("1 Anno", info.P1Y, info.V1Y, info.R1Y),
("3 Anni", info.P3Y, info.V3Y, info.R3Y),
("5 Anni", info.P5Y, info.V5Y, info.R5Y),
};
const float cellGap = 3f;
float cellW = (w - 2 * cellGap) / 3f;
const float cellH = 50f;
for (int i = 0; i < 6; i++)
{
int col = i % 3;
int row = i / 3;
float cx = x + col * (cellW + cellGap);
float cy = y + row * (cellH + cellGap);
var p = periods[i];
DrawPerfCell(g, p.Label, p.Perf, p.Vol, p.Rr, cx, cy, cellW, cellH);
}
}
private void DrawPerfCell(PdfGraphics g, string period,
decimal? perf, decimal? vol, decimal? rr,
float x, float y, float w, float h)
{
var cellBg = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 248, 250, 255)));
var cellPen = new PdfPen(new PdfColor(Color.FromArgb(255, 221, 227, 240)), 0.5f);
g.DrawRectangle(cellBg, new RectangleF(x, y, w, h));
g.DrawRectangle(cellPen, new RectangleF(x, y, w, h));
var center = new PdfStringFormat
{
Alignment = PdfTextAlignment.Center,
LineAlignment = PdfVerticalAlignment.Top
};
g.DrawString(period, PdfTheme.SmallBold, PdfTheme.AccentBlueBrush,
new RectangleF(x, y + 2f, w, 12f), center);
if (perf.HasValue)
{
var perfBrush = perf.Value < 0
? new PdfSolidBrush(new PdfColor(PdfTheme.NegativeRed))
: new PdfSolidBrush(new PdfColor(PdfTheme.PositiveGreen));
g.DrawString($"{perf.Value:F2}%", PdfTheme.TableBold, perfBrush,
new RectangleF(x, y + 14f, w, 12f), center);
}
else
g.DrawString("—", PdfTheme.TableFont,
new PdfSolidBrush(new PdfColor(PdfTheme.TextSecondary)),
new RectangleF(x, y + 14f, w, 12f), center);
var volText = vol.HasValue ? $"Vol: {vol.Value:F2}%" : "Vol: —";
g.DrawString(volText, PdfTheme.Small,
new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 85, 85, 85))),
new RectangleF(x, y + 26f, w, 12f), center);
g.DrawLine(new PdfPen(new PdfColor(Color.FromArgb(255, 238, 238, 238)), 0.5f),
new PointF(x + 4f, y + h - 15f), new PointF(x + w - 4f, y + h - 15f));
var rrText = rr.HasValue ? $"R/R: {rr.Value:F2}" : "R/R: —";
g.DrawString(rrText, PdfTheme.Small,
new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 136, 136, 136))),
new RectangleF(x, y + h - 14f, w, 12f), center);
}
private float DrawColHeader(PdfGraphics g, string title, float x, float w, float y)
{
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(x, y, 3f, 14f));
g.DrawString(title, PdfTheme.Bold, new PdfSolidBrush(new PdfColor(PdfTheme.AccentBlue)),
new RectangleF(x + 6f, y, w - 6f, 14f));
g.DrawLine(new PdfPen(new PdfColor(PdfTheme.AccentBlue), 0.5f),
new PointF(x, y + 15f), new PointF(x + w, y + 15f));
return y + 18f;
}
private float DrawGridHeader(PdfGraphics g, string title, float x, float w, float y)
{
const float h = 16f;
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(x, y, w, h));
var fmt = new PdfStringFormat
{
Alignment = PdfTextAlignment.Left,
LineAlignment = PdfVerticalAlignment.Middle
};
g.DrawString(title, PdfTheme.SmallBold,
new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 255, 255, 255))),
new RectangleF(x + 4f, y, w - 6f, h), fmt);
return y + h + 4f;
}
private void DrawKvList(PdfGraphics g, (string Label, string Value)[] items,
float x, float w, float y)
{
float rh = PdfTheme.RowHeight;
float labelW = w * 0.52f;
float valueW = w * 0.48f;
var labelBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 85, 85, 85)));
var valueBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 33, 33, 33)));
foreach (var (label, value) in items)
{
g.DrawString(label + ":", PdfTheme.TableBold, labelBrush,
new RectangleF(x, y, labelW, rh));
g.DrawString(value, PdfTheme.TableFont, valueBrush,
new RectangleF(x + labelW, y, valueW, rh));
y += rh;
}
}
}