161 lines
6.0 KiB
C#
161 lines
6.0 KiB
C#
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;
|
||
}
|