feat: add DividendSectionRenderer with landscape two-level header table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
using CertReports.Syncfusion.Helpers;
|
||||
using CertReports.Syncfusion.Models;
|
||||
using Syncfusion.Drawing;
|
||||
using Syncfusion.Pdf;
|
||||
using Syncfusion.Pdf.Graphics;
|
||||
using Syncfusion.Pdf.Grid;
|
||||
|
||||
namespace CertReports.Syncfusion.Services.Implementations;
|
||||
|
||||
/// <summary>
|
||||
/// Genera la pagina landscape con la tabella unificata Sottostanti + Dividendi.
|
||||
/// Non implementa IPdfSectionRenderer — iniettato direttamente nell'orchestratore.
|
||||
/// </summary>
|
||||
public class DividendSectionRenderer
|
||||
{
|
||||
// Larghezze colonne indicative (totale ~742pt) — scalate proporzionalmente a runtime
|
||||
private static readonly float[] ColWidths =
|
||||
[
|
||||
90f, // 0 Nome
|
||||
50f, // 1 Strike
|
||||
50f, // 2 Last
|
||||
46f, // 3 % Perf.
|
||||
50f, // 4 Barr.Cap.
|
||||
46f, // 5 Buf.Cap.
|
||||
50f, // 6 Trig.CPN
|
||||
46f, // 7 Buf.CPN
|
||||
46f, // 8 Trig.AC
|
||||
54f, // 9 Data Stacco
|
||||
54f, // 10 Data Pag.
|
||||
40f, // 11 Importo
|
||||
40f, // 12 Rend.
|
||||
40f, // 13 Imp.Fut.
|
||||
40f, // 14 Rend.Fut.
|
||||
];
|
||||
|
||||
private static readonly string[] Col2Headers =
|
||||
[
|
||||
"", "Strike", "Last", "% Perf.",
|
||||
"Barr.Cap.", "Buf.Cap.", "Trig.CPN", "Buf.CPN", "Trig.AC",
|
||||
"Data Stacco", "Data Pag.", "Importo", "Rend.", "Imp.Fut.", "Rend.Fut.",
|
||||
];
|
||||
|
||||
// Indici colonne soggette a colore performance (0-based)
|
||||
private static readonly HashSet<int> PerfCols = [3, 5, 7, 12, 14];
|
||||
|
||||
// Static brush e pen per header
|
||||
private static readonly PdfSolidBrush DarkBlueBrush = new(Color.FromArgb(255, 10, 56, 128)); // #0A3880
|
||||
private static readonly PdfPen SeparatorPen = new(Color.FromArgb(255, 100, 181, 246), 1.5f); // #64B5F6
|
||||
|
||||
public PdfDocument Render(CertificateReportData data)
|
||||
{
|
||||
var doc = new PdfDocument();
|
||||
doc.PageSettings.Orientation = PdfPageOrientation.Landscape;
|
||||
doc.PageSettings.Size = PdfPageSize.A4;
|
||||
|
||||
var page = doc.Pages.Add();
|
||||
var g = page.Graphics;
|
||||
var size = page.GetClientSize();
|
||||
|
||||
float pageWidth = size.Width;
|
||||
float pageHeight = size.Height;
|
||||
|
||||
float w = pageWidth - 2 * PdfTheme.PageMargin;
|
||||
float h = pageHeight - 2 * PdfTheme.PageMargin - PdfTheme.FooterHeight;
|
||||
float x0 = PdfTheme.PageMargin;
|
||||
float y = PdfTheme.PageMargin;
|
||||
|
||||
// ── Titolo ────────────────────────────────────────────────────
|
||||
g.DrawString("Sottostanti e Dividendi", PdfTheme.Title, PdfTheme.AccentBlueBrush,
|
||||
new RectangleF(x0, y, w, 20f));
|
||||
y += 20f + 4f;
|
||||
|
||||
// ── Linea separatrice ─────────────────────────────────────────
|
||||
g.DrawLine(PdfTheme.AccentBluePen, new PointF(x0, y), new PointF(x0 + w, y));
|
||||
y += 6f;
|
||||
|
||||
// ── Calcolo fattore scala su larghezza reale ──────────────────
|
||||
float totalNominal = ColWidths.Sum();
|
||||
float scale = w / totalNominal;
|
||||
float[] cw = ColWidths.Select(c => c * scale).ToArray();
|
||||
|
||||
// ── Disegno header a 2 livelli ────────────────────────────────
|
||||
float rh = PdfTheme.RowHeight;
|
||||
|
||||
DrawHeader(g, x0, y, cw, rh);
|
||||
|
||||
y += rh * 2;
|
||||
|
||||
// ── Tabella dati ──────────────────────────────────────────────
|
||||
DrawDataGrid(g, data, x0, y, cw);
|
||||
|
||||
// ── Footer ────────────────────────────────────────────────────
|
||||
PdfTheme.DrawFooter(g, pageWidth, pageHeight, 1, data.ShowBranding);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void DrawHeader(PdfGraphics g, float x0, float y, float[] cw, float rh)
|
||||
{
|
||||
var headerFont = PdfTheme.TableBold;
|
||||
var whiteBrush = PdfBrushes.White;
|
||||
var accentBrush = PdfTheme.AccentBlueBrush;
|
||||
|
||||
// ── Riga 1: Gruppi ─────────────────────────────────────────────
|
||||
float cx = x0;
|
||||
|
||||
// "Nome" (col 0) — cella vuota
|
||||
DrawHeaderCell(g, cx, y, cw[0], rh, "", accentBrush, whiteBrush, headerFont);
|
||||
cx += cw[0];
|
||||
|
||||
// "SOTTOSTANTE" (cols 1-3)
|
||||
float sottostanteW = cw[1] + cw[2] + cw[3];
|
||||
DrawHeaderCell(g, cx, y, sottostanteW, rh, "SOTTOSTANTE", accentBrush, whiteBrush, headerFont);
|
||||
cx += sottostanteW;
|
||||
|
||||
// "BARRIERE" (cols 4-8)
|
||||
float barriereW = cw[4] + cw[5] + cw[6] + cw[7] + cw[8];
|
||||
DrawHeaderCell(g, cx, y, barriereW, rh, "BARRIERE", accentBrush, whiteBrush, headerFont);
|
||||
cx += barriereW;
|
||||
|
||||
// "DIVIDENDI" (cols 9-14) — blu scuro
|
||||
float dividendiW = cw[9] + cw[10] + cw[11] + cw[12] + cw[13] + cw[14];
|
||||
DrawHeaderCell(g, cx, y, dividendiW, rh, "DIVIDENDI", darkBlueBrush, whiteBrush, headerFont);
|
||||
|
||||
// ── Riga 2: Sottocolonne ───────────────────────────────────────
|
||||
cx = x0;
|
||||
for (int i = 0; i < cw.Length; i++)
|
||||
{
|
||||
var bg = i >= 9 ? darkBlueBrush : accentBrush;
|
||||
DrawHeaderCell(g, cx, y + rh, cw[i], rh, Col2Headers[i], bg, whiteBrush, headerFont);
|
||||
cx += cw[i];
|
||||
}
|
||||
|
||||
// ── Separatore verticale tra col 8 e col 9 ────────────────────
|
||||
float sepX = x0 + cw.Take(9).Sum();
|
||||
g.DrawLine(SeparatorPen, new PointF(sepX, y), new PointF(sepX, y + rh * 2));
|
||||
}
|
||||
|
||||
private static void DrawHeaderCell(PdfGraphics g, float x, float y, float w, float h,
|
||||
string text, PdfBrush bg, PdfBrush fg, PdfFont font)
|
||||
{
|
||||
g.DrawRectangle(bg, new RectangleF(x, y, w, h));
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var fmt = new PdfStringFormat
|
||||
{
|
||||
Alignment = PdfTextAlignment.Center,
|
||||
LineAlignment = PdfVerticalAlignment.Middle,
|
||||
};
|
||||
g.DrawString(text, font, fg, new RectangleF(x + 1, y, w - 2, h), fmt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawDataGrid(PdfGraphics g, CertificateReportData data,
|
||||
float x0, float y, float[] cw)
|
||||
{
|
||||
var grid = new PdfGrid();
|
||||
grid.Style.CellPadding = new PdfPaddings(2, 2, 2, 2);
|
||||
grid.Style.Font = PdfTheme.TableFont;
|
||||
|
||||
// 15 colonne
|
||||
grid.Columns.Add(15);
|
||||
for (int i = 0; i < 15; i++)
|
||||
grid.Columns[i].Width = cw[i];
|
||||
|
||||
var sottostanti = data.Info.Sottostanti;
|
||||
|
||||
for (int i = 0; i < sottostanti.Count; i++)
|
||||
{
|
||||
var s = sottostanti[i];
|
||||
var row = grid.Rows.Add();
|
||||
|
||||
row.Cells[0].Value = s.Nome;
|
||||
row.Cells[1].Value = s.Strike;
|
||||
row.Cells[2].Value = s.LastPrice;
|
||||
row.Cells[3].Value = string.IsNullOrEmpty(s.Performance) ? "—" : s.Performance;
|
||||
row.Cells[4].Value = string.IsNullOrEmpty(s.CapitalBarrier) ? "—" : s.CapitalBarrier;
|
||||
row.Cells[5].Value = string.IsNullOrEmpty(s.ULCapitalBarrierBuffer) ? "—" : s.ULCapitalBarrierBuffer;
|
||||
row.Cells[6].Value = string.IsNullOrEmpty(s.CouponBarrier) ? "—" : s.CouponBarrier;
|
||||
row.Cells[7].Value = string.IsNullOrEmpty(s.ULCouponBarrierBuffer) ? "—" : s.ULCouponBarrierBuffer;
|
||||
row.Cells[8].Value = string.IsNullOrEmpty(s.TriggerAutocall) ? "—" : s.TriggerAutocall;
|
||||
row.Cells[9].Value = string.IsNullOrEmpty(s.DividendExDate) ? "—" : s.DividendExDate;
|
||||
row.Cells[10].Value = string.IsNullOrEmpty(s.DividendPayDate) ? "—" : s.DividendPayDate;
|
||||
row.Cells[11].Value = string.IsNullOrEmpty(s.DividendAmount) ? "—" : s.DividendAmount;
|
||||
row.Cells[12].Value = string.IsNullOrEmpty(s.DividendYield) ? "—" : s.DividendYield;
|
||||
row.Cells[13].Value = string.IsNullOrEmpty(s.DividendFutAmount) ? "—" : s.DividendFutAmount;
|
||||
row.Cells[14].Value = string.IsNullOrEmpty(s.DividendFutYield) ? "—" : s.DividendFutYield;
|
||||
|
||||
// Righe alternate
|
||||
if (i % 2 == 1)
|
||||
{
|
||||
for (int c = 0; c < 15; c++)
|
||||
row.Cells[c].Style.BackgroundBrush = PdfTheme.TableAltRowBrush;
|
||||
}
|
||||
|
||||
// Colore performance: negativi rosso, positivi verde
|
||||
foreach (int c in PerfCols)
|
||||
{
|
||||
var val = row.Cells[c].Value?.ToString();
|
||||
if (string.IsNullOrEmpty(val) || val == "—") continue;
|
||||
|
||||
bool isNegative = val.TrimStart().StartsWith('-');
|
||||
row.Cells[c].Style.TextBrush = isNegative
|
||||
? PdfTheme.NegativeRedBrush
|
||||
: PdfTheme.PositiveGreenBrush;
|
||||
}
|
||||
|
||||
// Allineamento: Nome a sinistra, tutto il resto centrato
|
||||
row.Cells[0].StringFormat = new PdfStringFormat(PdfTextAlignment.Left);
|
||||
for (int c = 1; c < 15; c++)
|
||||
{
|
||||
row.Cells[c].StringFormat = new PdfStringFormat
|
||||
{
|
||||
Alignment = PdfTextAlignment.Center,
|
||||
LineAlignment = PdfVerticalAlignment.Middle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Pagina singola: specifica garantisce max ~20 sottostanti per certificato
|
||||
grid.Draw(g, new PointF(x0, y));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user