diff --git a/CertReports.Syncfusion/Controllers/FundChartController.cs b/CertReports.Syncfusion/Controllers/FundChartController.cs new file mode 100644 index 0000000..a4fc268 --- /dev/null +++ b/CertReports.Syncfusion/Controllers/FundChartController.cs @@ -0,0 +1,139 @@ +using CertReports.Syncfusion.Services.Implementations; +using CertReports.Syncfusion.Services.Interfaces; +using Microsoft.AspNetCore.Mvc; +using Syncfusion.Pdf; +using Syncfusion.Pdf.Graphics; + +namespace CertReports.Syncfusion.Controllers; + +/// +/// Endpoint: +/// GET /api/chart/fund/{isin}[?format=png|jpg|jpeg|pdf&width=&height=&save=true] +/// +[ApiController] +[Route("api/chart/fund")] +public class FundChartController : ControllerBase +{ + private readonly IFundDataService _dataService; + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + + public FundChartController( + IFundDataService dataService, + IConfiguration configuration, + ILogger logger) + { + _dataService = dataService; + _configuration = configuration; + _logger = logger; + } + + [HttpGet("{isin}")] + public async Task GetChart( + string isin, + [FromQuery] string format = "png", + [FromQuery] int width = 1100, + [FromQuery] int height = 700, + [FromQuery] bool save = false) + { + if (string.IsNullOrWhiteSpace(isin)) + return BadRequest("ISIN richiesto."); + + width = Math.Clamp(width, 400, 2000); + height = Math.Clamp(height, 300, 1500); + + try + { + var info = await _dataService.GetFundInfoAsync(isin); + var points = await _dataService.GetChartPricesAsync(isin); + + if (info == null || points.Count == 0) + return NotFound($"Nessun dato per ISIN {isin}"); + + bool isJpeg = format.Equals("jpg", StringComparison.OrdinalIgnoreCase) + || format.Equals("jpeg", StringComparison.OrdinalIgnoreCase); + + if (format.Equals("pdf", StringComparison.OrdinalIgnoreCase)) + { + var pngBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height); + var pdfBytes = WrapPngInPdf(pngBytes); + Response.Headers.Append("Content-Disposition", + $"inline; filename=chart_fund_{isin}.pdf"); + return File(pdfBytes, "application/pdf"); + } + + var imgBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height, jpeg: isJpeg); + + if (save && isJpeg) + await SaveChartToDiskAsync(isin, imgBytes); + + if (isJpeg) + { + Response.Headers.Append("Content-Disposition", + $"inline; filename=chart_fund_{isin}.jpg"); + return File(imgBytes, "image/jpeg"); + } + + Response.Headers.Append("Content-Disposition", + $"inline; filename=chart_fund_{isin}.png"); + return File(imgBytes, "image/png"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Errore generazione chart fondo per ISIN {Isin}", isin); + return StatusCode(500, new { status = "KO", message = "Errore nella generazione del grafico." }); + } + } + + private async Task SaveChartToDiskAsync(string isin, byte[] imgBytes) + { + try + { + string? folder = _configuration["ChartSettings:SavePath"]; + if (string.IsNullOrEmpty(folder)) + { + _logger.LogWarning("Salvataggio chart fondo saltato: ChartSettings:SavePath non configurato"); + return; + } + + Directory.CreateDirectory(folder); + var fullPath = Path.Combine(folder, $"{isin}_fund.jpg"); + await System.IO.File.WriteAllBytesAsync(fullPath, imgBytes); + _logger.LogInformation("Chart fondo salvato in {Path}", fullPath); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Salvataggio chart fondo fallito per {Isin}", isin); + } + } + + private static byte[] WrapPngInPdf(byte[] pngBytes) + { + var doc = new PdfDocument(); + doc.PageSettings.Size = PdfPageSize.A4; + doc.PageSettings.Orientation = PdfPageOrientation.Landscape; + doc.PageSettings.Margins.All = 30; + + var page = doc.Pages.Add(); + var g = page.Graphics; + float pw = page.GetClientSize().Width; + float ph = page.GetClientSize().Height; + + using var stream = new MemoryStream(pngBytes); + var img = new PdfBitmap(stream); + + float ratio = (float)img.Width / img.Height; + float dw = pw; + float dh = dw / ratio; + if (dh > ph) { dh = ph; dw = dh * ratio; } + + float x = (pw - dw) / 2; + float y = (ph - dh) / 2; + g.DrawImage(img, x, y, dw, dh); + + using var output = new MemoryStream(); + doc.Save(output); + doc.Close(true); + return output.ToArray(); + } +}