Files
PIbd-32_BuslaevRoman_KOP/ReportPlugin.PiePdf/PieChartPdfReport.cs
2025-10-21 13:17:55 +04:00

161 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ComponentOrientedPlatform.Abstractions;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
using MigraDoc.Rendering;
namespace ReportPlugin.PiePdf;
public sealed class PieChartPdfReport : IReportDocumentWithChartPieContract
{
public string DocumentFormat => "pdf";
public async Task CreateDocumentAsync(
string filePath,
string header,
string chartTitle,
List<(int Parameter, double Value)> series)
{
if (string.IsNullOrWhiteSpace(filePath))
throw new ArgumentNullException(nameof(filePath));
if (string.IsNullOrWhiteSpace(header))
throw new ArgumentNullException(nameof(header));
if (string.IsNullOrWhiteSpace(chartTitle))
throw new ArgumentNullException(nameof(chartTitle));
if (series == null)
throw new ArgumentNullException(nameof(series));
if (series.Count == 0)
throw new ArgumentOutOfRangeException(nameof(series), "Список серий пуст.");
var values = series.Select(s => SanitizeValue(s.Value)).ToList();
var labels = Enumerable.Range(1, values.Count).Select(i => $"Категория {i}").ToList();
await GeneratePdfAsync(filePath, header, chartTitle, labels, values);
}
public async Task CreateDocumentAsync(
string filePath,
string header,
string chartTitle,
IReadOnlyList<string> labels,
IReadOnlyList<double> values)
{
if (labels == null) throw new ArgumentNullException(nameof(labels));
if (values == null) throw new ArgumentNullException(nameof(values));
if (labels.Count != values.Count)
throw new ArgumentException("Длины labels и values должны совпадать.");
var vals = values.Select(SanitizeValue).ToList();
await GeneratePdfAsync(filePath, header, chartTitle, labels.ToList(), vals);
}
[Obsolete]
private async Task GeneratePdfAsync(
string filePath,
string header,
string chartTitle,
List<string> labels,
List<double> values)
{
var items = labels.Zip(values, (l, v) => new { Label = (l ?? "").Trim(), Value = v })
.Where(x => x.Value > 0 && !double.IsNaN(x.Value) && !double.IsInfinity(x.Value))
.OrderByDescending(x => x.Value)
.ToList();
if (items.Count == 0)
throw new InvalidOperationException("Нет валидных данных для диаграммы.");
var doc = new Document();
doc.Info.Title = header;
doc.UseCmykColor = false;
var normal = doc.Styles["Normal"];
normal.Font.Name = "Arial";
normal.Font.Size = 10;
var heading1 = doc.Styles["Heading1"];
heading1.Font.Name = normal.Font.Name;
heading1.Font.Size = 16;
heading1.Font.Bold = true;
heading1.ParagraphFormat.SpaceAfter = "0.5cm";
heading1.ParagraphFormat.Alignment = ParagraphAlignment.Center;
var sec = doc.AddSection();
sec.PageSetup.PageFormat = PageFormat.A4;
sec.PageSetup.TopMargin = Unit.FromCentimeter(2);
sec.PageSetup.BottomMargin = Unit.FromCentimeter(2);
sec.PageSetup.LeftMargin = Unit.FromCentimeter(2);
sec.PageSetup.RightMargin = Unit.FromCentimeter(2);
sec.AddParagraph(header, "Heading1");
var sub = sec.AddParagraph($"Сформировано: {DateTime.Now:dd.MM.yyyy HH:mm}", "Normal");
sub.Format.Alignment = ParagraphAlignment.Right;
sub.Format.SpaceAfter = "0.5cm";
var chart = sec.AddChart(ChartType.Pie2D);
chart.LineFormat.Color = Colors.DarkGray;
chart.LineFormat.Width = Unit.FromPoint(0.6);
chart.Width = Unit.FromCentimeter(16);
chart.Height = Unit.FromCentimeter(10);
chart.PlotArea.TopPadding = Unit.FromCentimeter(0.8);
chart.PlotArea.RightPadding = Unit.FromCentimeter(0.8);
chart.PlotArea.BottomPadding = Unit.FromCentimeter(0.8);
chart.PlotArea.LeftPadding = Unit.FromCentimeter(0.8);
var s = chart.SeriesCollection.AddSeries();
s.HasDataLabel = true;
s.DataLabel.Type = DataLabelType.Percent;
s.DataLabel.Position = DataLabelPosition.OutsideEnd;
s.DataLabel.Font.Size = 9;
var palette = new[]
{
Colors.DodgerBlue, Colors.MediumOrchid, Colors.LimeGreen, Colors.Chocolate,
Colors.OrangeRed, Colors.Gold, Colors.SteelBlue, Colors.MediumSeaGreen
};
for (int i = 0; i < items.Count; i++)
{
var p = s.Add(items[i].Value);
p.FillFormat.Color = palette[i % palette.Length];
p.LineFormat.Color = Colors.White;
p.LineFormat.Width = Unit.FromPoint(0.5);
}
var xSeries = chart.XValues.AddXSeries();
foreach (var it in items)
xSeries.Add(it.Label);
var legend = chart.RightArea.AddLegend();
legend.LineFormat.Color = Colors.DarkGray;
legend.LineFormat.Width = Unit.FromPoint(0.6);
legend.Format.Font.Size = 9;
chart.TopArea.AddParagraph(chartTitle).Format.Alignment = ParagraphAlignment.Center;
var sum = items.Sum(x => x.Value);
var note = sec.AddParagraph($"Всего: {sum.ToString("N0", CultureInfo.CurrentCulture)}", "Normal");
note.Format.SpaceBefore = "0.3cm";
note.Format.Alignment = ParagraphAlignment.Left;
var renderer = new PdfDocumentRenderer(unicode: true) { Document = doc };
await Task.Run(() =>
{
renderer.RenderDocument();
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
renderer.Save(filePath);
});
}
private static double SanitizeValue(double v)
=> double.IsNaN(v) || double.IsInfinity(v) || v < 0 ? 0d : v;
}