|
|
|
|
@@ -11,11 +11,19 @@ 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;
|
|
|
|
|
private const float Col1W = 247f;
|
|
|
|
|
private const float Col2W = 78f; // ESG ridotto per dare spazio a Dati Anagrafici
|
|
|
|
|
private const float Col3W = PageW - Col1W - Col2W - 2 * ColGap; // = 174
|
|
|
|
|
|
|
|
|
|
public PdfDocument Render(FundReportData data)
|
|
|
|
|
// Strip box widths — usati sia nello strip che per allineare titolo e Col3
|
|
|
|
|
private const float StripW1 = 100f; // ISIN
|
|
|
|
|
private const float StripW2 = 225f; // CATEGORIA
|
|
|
|
|
private const float StripGap = 8f;
|
|
|
|
|
private const float StripTitleW = StripW1 + StripGap + StripW2; // = 333 → bordo destro titolo = bordo destro CATEGORIA
|
|
|
|
|
private const float StripBoxX = StripTitleW + StripGap; // = 341 → x di RANKING e box combinato
|
|
|
|
|
private const float StripW3 = PageW - StripBoxX; // = 174 → RANKING e box combinato fino al bordo pagina
|
|
|
|
|
|
|
|
|
|
public PdfDocument Render(FundReportData data, byte[]? chartPng = null)
|
|
|
|
|
{
|
|
|
|
|
var doc = new PdfDocument();
|
|
|
|
|
var page = PdfTheme.AddA4Page(doc);
|
|
|
|
|
@@ -28,13 +36,15 @@ public class FundAnagraficaRenderer
|
|
|
|
|
y = DrawStrip(g, info, y);
|
|
|
|
|
y += 8f;
|
|
|
|
|
|
|
|
|
|
float col1X = 0f;
|
|
|
|
|
float col2X = Col1W + ColGap;
|
|
|
|
|
float col3X = col2X + Col2W + ColGap;
|
|
|
|
|
float contentY = y;
|
|
|
|
|
float col1Bottom = DrawAnagrafici(g, info, 0f, Col1W, contentY);
|
|
|
|
|
float col2Bottom = DrawEsg (g, info, Col1W + ColGap, Col2W, contentY);
|
|
|
|
|
float col3Bottom = DrawPerfGrid (g, info, Col1W + ColGap + Col2W + ColGap, Col3W, contentY);
|
|
|
|
|
|
|
|
|
|
DrawAnagrafici(g, info, col1X, Col1W, y);
|
|
|
|
|
DrawEsg(g, info, col2X, Col2W, y);
|
|
|
|
|
DrawPerfGrid(g, info, col3X, Col3W, y);
|
|
|
|
|
y = Math.Max(col1Bottom, Math.Max(col2Bottom, col3Bottom)) + 14f;
|
|
|
|
|
|
|
|
|
|
if (chartPng != null)
|
|
|
|
|
DrawChartImage(g, chartPng, y);
|
|
|
|
|
|
|
|
|
|
PdfTheme.DrawFooter(g, PageW, PageH, 1, data.ShowBranding);
|
|
|
|
|
return doc;
|
|
|
|
|
@@ -42,37 +52,42 @@ public class FundAnagraficaRenderer
|
|
|
|
|
|
|
|
|
|
private float DrawTitle(PdfGraphics g, FundInfo info, float y)
|
|
|
|
|
{
|
|
|
|
|
const float h = 22f;
|
|
|
|
|
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(0, y, PageW, h));
|
|
|
|
|
const float h = 36f;
|
|
|
|
|
|
|
|
|
|
// Barra blu titolo — bordo destro allineato con CATEGORIA
|
|
|
|
|
g.DrawRectangle(PdfTheme.AccentBlueBrush, new RectangleF(0, y, StripTitleW, h));
|
|
|
|
|
var fmt = new PdfStringFormat
|
|
|
|
|
{
|
|
|
|
|
Alignment = PdfTextAlignment.Center,
|
|
|
|
|
Alignment = PdfTextAlignment.Left,
|
|
|
|
|
LineAlignment = PdfVerticalAlignment.Middle
|
|
|
|
|
};
|
|
|
|
|
var title = $"{info.Tipo ?? "Fondo"} — {info.Strumento} — {info.Isin}";
|
|
|
|
|
g.DrawString(title, PdfTheme.Bold,
|
|
|
|
|
var tipo = (info.Tipo ?? "FONDO").ToUpperInvariant();
|
|
|
|
|
g.DrawString($"{tipo} — {info.Strumento}", PdfTheme.SectionTitleFont,
|
|
|
|
|
new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 255, 255, 255))),
|
|
|
|
|
new RectangleF(0, y, PageW, h), fmt);
|
|
|
|
|
new RectangleF(6f, y, StripTitleW - 6f, h), fmt);
|
|
|
|
|
|
|
|
|
|
// Box combinato PREZZO + AGGIORNATO AL — stesso x/w di RANKING
|
|
|
|
|
var prezzoStr = info.Prezzo.HasValue ? $"{info.Prezzo.Value:F2} {info.Valuta}" : "—";
|
|
|
|
|
var dataStr = info.DataPrezzo.HasValue ? info.DataPrezzo.Value.ToString("dd/MM/yyyy") : "—";
|
|
|
|
|
DrawCombinedStripBox(g, "PREZZO", prezzoStr, "AGGIORNATO AL", dataStr,
|
|
|
|
|
StripBoxX, y, StripW3, h);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
const float h = 36f;
|
|
|
|
|
|
|
|
|
|
DrawStripBox(g, "RATING / RANK",
|
|
|
|
|
DrawStripBox(g, "ISIN",
|
|
|
|
|
info.Isin,
|
|
|
|
|
0f, y, StripW1, h);
|
|
|
|
|
DrawStripBox(g, "CATEGORIA",
|
|
|
|
|
info.CategoriaMorningstar ?? "—",
|
|
|
|
|
StripW1 + StripGap, y, StripW2, h);
|
|
|
|
|
DrawStripBox(g, "RANKING",
|
|
|
|
|
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);
|
|
|
|
|
StripBoxX, y, StripW3, h);
|
|
|
|
|
|
|
|
|
|
return y + h;
|
|
|
|
|
}
|
|
|
|
|
@@ -82,7 +97,7 @@ public class FundAnagraficaRenderer
|
|
|
|
|
{
|
|
|
|
|
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(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)));
|
|
|
|
|
@@ -93,29 +108,57 @@ public class FundAnagraficaRenderer
|
|
|
|
|
new RectangleF(x + 6f, y + 13f, w - 8f, h - 15f), leftFmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawAnagrafici(PdfGraphics g, FundInfo info, float x, float w, float y)
|
|
|
|
|
// Un unico box con due coppie label/valore affiancate sulla stessa riga
|
|
|
|
|
private void DrawCombinedStripBox(PdfGraphics g,
|
|
|
|
|
string label1, string value1, string label2, string value2,
|
|
|
|
|
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 };
|
|
|
|
|
float half = w / 2f;
|
|
|
|
|
|
|
|
|
|
// Sinistra: PREZZO
|
|
|
|
|
g.DrawString(label1, PdfTheme.Small, grayBrush,
|
|
|
|
|
new RectangleF(x + 6f, y + 2f, half - 8f, 10f), leftFmt);
|
|
|
|
|
g.DrawString(value1, PdfTheme.Bold, PdfTheme.AccentBlueBrush,
|
|
|
|
|
new RectangleF(x + 6f, y + 13f, half - 8f, h - 15f), leftFmt);
|
|
|
|
|
|
|
|
|
|
// Destra: AGGIORNATO AL
|
|
|
|
|
g.DrawString(label2, PdfTheme.Small, grayBrush,
|
|
|
|
|
new RectangleF(x + half + 2f, y + 2f, half - 6f, 10f), leftFmt);
|
|
|
|
|
g.DrawString(value2, PdfTheme.Bold, PdfTheme.AccentBlueBrush,
|
|
|
|
|
new RectangleF(x + half + 2f, y + 13f, half - 6f, h - 15f), leftFmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float 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 ?? "—"),
|
|
|
|
|
("Società", info.Societa ?? "—"),
|
|
|
|
|
("Tipo", info.Tipo?.ToUpperInvariant() ?? "—"),
|
|
|
|
|
("Valuta", info.Valuta ?? "—"),
|
|
|
|
|
("Hedged", string.IsNullOrEmpty(info.Hedged) ? "—" : info.Hedged),
|
|
|
|
|
("Benchmark", info.Benchmark ?? "—"),
|
|
|
|
|
("Benchmark", info.Benchmark ?? "—"),
|
|
|
|
|
("Spese correnti", info.SpeseCorrenti.HasValue ? $"{info.SpeseCorrenti.Value:F2}%" : "—"),
|
|
|
|
|
("Catalogo", info.Catalogo ?? "—"),
|
|
|
|
|
("Proventi", info.Proventi ?? "—"),
|
|
|
|
|
("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);
|
|
|
|
|
return y + items.Length * PdfTheme.RowHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawEsg(PdfGraphics g, FundInfo info, float x, float w, float y)
|
|
|
|
|
private float DrawEsg(PdfGraphics g, FundInfo info, float x, float w, float y)
|
|
|
|
|
{
|
|
|
|
|
y = DrawColHeader(g, "ESG Score", x, w, y);
|
|
|
|
|
|
|
|
|
|
@@ -144,11 +187,14 @@ public class FundAnagraficaRenderer
|
|
|
|
|
new RectangleF(x + 5f, y + 14f, w - 8f, 20f));
|
|
|
|
|
y += cardH + 3f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawPerfGrid(PdfGraphics g, FundInfo info, float x, float w, float y)
|
|
|
|
|
private float DrawPerfGrid(PdfGraphics g, FundInfo info, float x, float w, float y)
|
|
|
|
|
{
|
|
|
|
|
y = DrawGridHeader(g, "PERFORMANCE · VOLATILITÀ · REND/RISK", x, w, y);
|
|
|
|
|
// Intestazione con barretta blu laterale (stesso stile di Dati Anagrafici e ESG)
|
|
|
|
|
y = DrawColHeader(g, "PERFORMANCE · VOLATILITÀ · REND/RISK", x, w, y);
|
|
|
|
|
|
|
|
|
|
var periods = new (string Label, decimal? Perf, decimal? Vol, decimal? Rr)[]
|
|
|
|
|
{
|
|
|
|
|
@@ -161,8 +207,8 @@ public class FundAnagraficaRenderer
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const float cellGap = 3f;
|
|
|
|
|
const float cellH = 50f;
|
|
|
|
|
float cellW = (w - 2 * cellGap) / 3f;
|
|
|
|
|
const float cellH = 50f;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
{
|
|
|
|
|
@@ -173,6 +219,18 @@ public class FundAnagraficaRenderer
|
|
|
|
|
var p = periods[i];
|
|
|
|
|
DrawPerfCell(g, p.Label, p.Perf, p.Vol, p.Rr, cx, cy, cellW, cellH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return y + 2 * (cellH + cellGap) - cellGap; // 2 righe
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawChartImage(PdfGraphics g, byte[] pngBytes, float y)
|
|
|
|
|
{
|
|
|
|
|
float chartH = PageH - y - 4f;
|
|
|
|
|
if (chartH < 60f) return;
|
|
|
|
|
|
|
|
|
|
using var imgStream = new MemoryStream(pngBytes);
|
|
|
|
|
var pdfImage = PdfImage.FromStream(imgStream);
|
|
|
|
|
g.DrawImage(pdfImage, new RectangleF(0f, y, PageW, chartH));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawPerfCell(PdfGraphics g, string period,
|
|
|
|
|
@@ -230,27 +288,12 @@ public class FundAnagraficaRenderer
|
|
|
|
|
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;
|
|
|
|
|
float labelW = w * 0.30f;
|
|
|
|
|
float valueW = w * 0.70f;
|
|
|
|
|
var labelBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 85, 85, 85)));
|
|
|
|
|
var valueBrush = new PdfSolidBrush(new PdfColor(Color.FromArgb(255, 33, 33, 33)));
|
|
|
|
|
|
|
|
|
|
|