diff --git a/Cop.Borovkov.Var3/Cop.Borovkov.Var3.sln b/Cop.Borovkov.Var3/Cop.Borovkov.Var3.sln index 585063b..12f71ea 100644 --- a/Cop.Borovkov.Var3/Cop.Borovkov.Var3.sln +++ b/Cop.Borovkov.Var3/Cop.Borovkov.Var3.sln @@ -5,7 +5,9 @@ VisualStudioVersion = 17.10.35122.118 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cop.Borovkov.Var3", "Cop.Borovkov.Var3\Cop.Borovkov.Var3.csproj", "{A8604186-0CDE-4504-805B-46104141269A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCustomComponents", "TestCustomComponents\TestCustomComponents.csproj", "{E2C46D08-ACCE-4547-922B-E92AD76D99C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestCustomComponents", "TestCustomComponents\TestCustomComponents.csproj", "{E2C46D08-ACCE-4547-922B-E92AD76D99C8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PIHelperSh.PdfCreator", "PIHelperSh.PdfCreater\PIHelperSh.PdfCreator.csproj", "{572BD835-A500-43C9-A66F-648540F4A1C8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {E2C46D08-ACCE-4547-922B-E92AD76D99C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2C46D08-ACCE-4547-922B-E92AD76D99C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2C46D08-ACCE-4547-922B-E92AD76D99C8}.Release|Any CPU.Build.0 = Release|Any CPU + {572BD835-A500-43C9-A66F-648540F4A1C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {572BD835-A500-43C9-A66F-648540F4A1C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.Designer.cs b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.Designer.cs new file mode 100644 index 0000000..497ec19 --- /dev/null +++ b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.Designer.cs @@ -0,0 +1,36 @@ +namespace Cop.Borovkov.Var3.Components +{ + partial class CustomPdfTable + { + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором компонентов + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.cs b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.cs new file mode 100644 index 0000000..0af6728 --- /dev/null +++ b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Components/CustomPdfTable.cs @@ -0,0 +1,92 @@ +using Cop.Borovkov.Var3.Models; +using PIHelperSh.PdfCreator; +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; +using PIHelperSh.PdfCreator.Models.TableModels; +using System.ComponentModel; + +namespace Cop.Borovkov.Var3.Components +{ + /// + /// Компонент для сохранения таблицы в пдф + /// + public partial class CustomPdfTable : Component + { + /// + /// + public CustomPdfTable() + { + InitializeComponent(); + } + + /// + /// + /// + public CustomPdfTable(IContainer container) + { + container.Add(this); + InitializeComponent(); + } + + /// + /// Сохранить набор таблиц в пдф + /// + /// + /// + /// + public void SaveToPdf(PdfTableInfo tableInfo) + { + if (!tableInfo.Tables.Any()) + { + return; + } + + PdfCreator creator = new PdfCreator(); + + creator.AddParagraph(new() + { + Style = PdfStyleType.Title, + Text = tableInfo.Title, + MarginAfter = PdfMargin.Smal, + }); + + foreach (string[,] table in tableInfo.Tables) + { + List rows = new(); + + for (int i = 0; i < table.GetLength(0); ++i) + { + PDFSimpleTableRow row = new(); + + for (int j = 0; j < table.GetLength(1); ++j) + { + row.Items.Add(table[i, j]); + } + + rows.Add(row); + } + + creator.AddSimpleTable(new() + { + Header = rows.First().Items.Select(item => new PdfTableColumn() + { + Title = item, + Size = 3, + } as IPdfColumnItem).ToList(), + Rows = rows.Skip(1).ToList(), + + HeaderStyle = PdfStyleType.Basic, + HeaderHorizontalAlignment = PdfAlignmentType.Left, + RowHorizontalAlignment = PdfAlignmentType.Left, + }); + + creator.AddParagraph(new() + { + MarginAfter = PdfMargin.Smal, + }); + } + + creator.SavePdf(tableInfo.FilePath); + } + } +} diff --git a/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Cop.Borovkov.Var3.csproj b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Cop.Borovkov.Var3.csproj index 3e210aa..a56206d 100644 --- a/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Cop.Borovkov.Var3.csproj +++ b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Cop.Borovkov.Var3.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Models/PdfTableInfo.cs b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Models/PdfTableInfo.cs new file mode 100644 index 0000000..fa93c5a --- /dev/null +++ b/Cop.Borovkov.Var3/Cop.Borovkov.Var3/Models/PdfTableInfo.cs @@ -0,0 +1,23 @@ +namespace Cop.Borovkov.Var3.Models +{ + /// + /// Параметры для создания таблиц в пдф + /// + public record PdfTableInfo + { + /// + /// имя файла (включая путь до файла) + /// + public string FilePath { get; init; } = @"C:\pdfTable.pdf"; + + /// + /// название документа(заголовок в документе) + /// + public string Title { get; init; } = "Таблица"; + + /// + /// Список таблиц + /// + public IEnumerable Tables { get; init; } = []; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfAlignmentType.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfAlignmentType.cs new file mode 100644 index 0000000..62bc7f7 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfAlignmentType.cs @@ -0,0 +1,32 @@ +using MigraDoc.DocumentObjectModel; +using PIHelperSh.Core.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Enums +{ + /// + /// Тип выравнивания элементов + /// + public enum PdfAlignmentType + { + /// + /// По центру + /// + [TypeValue(ParagraphAlignment.Center)] + Center, + /// + /// По левому краю + /// + [TypeValue(ParagraphAlignment.Left)] + Left, + /// + /// По правому краю + /// + [TypeValue(ParagraphAlignment.Right)] + Rigth + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfLegendPosition.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfLegendPosition.cs new file mode 100644 index 0000000..1923ccb --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfLegendPosition.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Enums +{ + /// + /// Варианты расположения легенды + /// + public enum PdfLegendPosition + { + /// + /// Снизу(по умолчанию) + /// + Bottom, + /// + /// Справа + /// + Right, + /// + /// Слева + /// + Left, + /// + /// Сверху + /// + Top + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfMargin.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfMargin.cs new file mode 100644 index 0000000..6553cec --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfMargin.cs @@ -0,0 +1,36 @@ +using PIHelperSh.Core.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Enums +{ + /// + /// Типы отступов после элементов (вертикальный) + /// + public enum PdfMargin + { + /// + /// Отступа нет + /// + [TypeValue("ocm")] + None, + /// + /// Отступ небольшой + /// + [TypeValue("0.3cm")] + Smal, + /// + /// Отступ средний + /// + [TypeValue("1cm")] + Medium, + /// + /// Отступ большой + /// + [TypeValue("1.6cm")] + Big + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfStyleType.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfStyleType.cs new file mode 100644 index 0000000..57b4a5c --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Enums/PdfStyleType.cs @@ -0,0 +1,54 @@ +using PIHelperSh.Core.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Enums +{ + /// + /// Типы стилей в документе + /// + public enum PdfStyleType + { + /// + /// Основная часть документа + /// + [TypeValue("Normal")] + Basic, + /// + /// Название документа + /// + [TypeValue("NormalTitle")] + Title, + /// + /// Уровень в списке 1 + /// + [TypeValue("BulletList")] + [TypeValue(1)] + ListLevel1, + /// + /// Уровень в списке 2 + /// + [TypeValue("BulletList2")] + [TypeValue(2)] + ListLevel2, + /// + /// Уровень в списке 3 + /// + [TypeValue("BulletList3")] + [TypeValue(3)] + ListLevel3, + /// + /// Жирный шрифт + /// + [TypeValue("NormalBold")] + Bold, + /// + /// Мелкий шрифт + /// + [TypeValue("NormalSmall")] + Small + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfColumnItem.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfColumnItem.cs new file mode 100644 index 0000000..ba6ba55 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfColumnItem.cs @@ -0,0 +1,15 @@ +namespace PIHelperSh.PdfCreator.Interfaces +{ + /// + /// + public interface IPdfColumnItem + { + /// + /// + public float? Size { get; } + + /// + /// + public string? Title { get; set; } + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfCreator.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfCreator.cs new file mode 100644 index 0000000..a2d87b5 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfCreator.cs @@ -0,0 +1,64 @@ +using PIHelperSh.PdfCreator.Models.ImageModels; +using PIHelperSh.PdfCreator.Models.PieChartModel; +using PIHelperSh.PdfCreator.Models.TableModels; +using PIHelperSh.PdfCreator.Models.TextModels; + +namespace PIHelperSh.PdfCreator.Interfaces +{ + /// + /// Создатель pdf документа + /// + public interface IPdfCreator + { + /// + /// Создаём параграф + /// + /// Модель параграфа, который вставляем + public void AddParagraph(PdfParagraph paragraph); + + /// + /// Создаём маркированный список + /// + /// Модель списка(может быть многоуровневым). Max - 3 уровня + public void AddList(PdfList List); + + /// + /// Создаёт таблицу, с шапкой из 2-х строк(с группировками) + /// + /// Тип DTO, из которой берутся данные в таблицу + /// Модель самой таблицы + /// + public void AddTable(PdfTable header, bool rowHeaded = false); + + /// + /// Создаёт табличку, наподобие той, что с T, но проще + /// + /// + public void AddSimpleTable(PDFSimpleTable tableData); + + /// + /// Создаёт круговую диаграмму. + /// + /// Модель для круговой диаграммы + public void AddChart(PdfPieChartModel pieChart); + + /// + /// Добавляем на лист изображение. Можно по пути, можно по потоку, можно по Base64 строке + /// + /// Модель одного изображения + public void AddImage(PdfImage image); + + /// + /// Метод сохранения созданного PDF документа + /// + /// Поток MemoryStream с документом + public MemoryStream SavePdf(); + + /// + /// Метод сохранения созданного PDF документа в файл + /// + /// Имя файла и путь до него. Проверки на расширение нет + public void SavePdf(string filename); + + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfElement.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfElement.cs new file mode 100644 index 0000000..3d6fb42 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Interfaces/IPdfElement.cs @@ -0,0 +1,20 @@ +using PIHelperSh.PdfCreator.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Interfaces +{ + /// + /// Аналогично word этот интерфейс нужен для работы списков. + /// + public interface IPdfElement + { + /// + /// //Отступ после(может быть и у списков и у параграфов) + /// + PdfMargin MarginAfter { get; } + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/MiraDocImage.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/MiraDocImage.cs new file mode 100644 index 0000000..6aaa5ac --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/MiraDocImage.cs @@ -0,0 +1,75 @@ +namespace PIHelperSh.PdfCreator.Models.ImageModels +{ + /// + /// Удобный метод для хранения изображений (для создания Pdf документа можно использовать либо путь до фото, либо поток с данными о нём) + /// + public class MiraDocImage + { + /// + /// Путь до фото + /// + public string? Path { get; set; } = null; + + /// + /// Поток фото + /// + public Stream? Source + { + get => _source; + set + { + _source = value; + _b64Source = null; + } + } + + private Stream? _source; + + private string? _b64Source = null; + + /// + /// Создание изображения из пути + /// + /// Путь + /// + public static MiraDocImage CreateFromPath(string path) => new() { Path = path }; + + /// + /// Создания изображения из строки base64 + /// + /// изображение в формате base64 + /// + public static MiraDocImage CreateFromBase64(string base64) => new() { _b64Source = $"base64:{base64}" }; + + /// + /// Создание изображения из потока + /// + /// Поток + /// + public static MiraDocImage CreateFromStream(Stream stream) => new() { Source = stream }; + + /// + /// + /// + /// + public string GetImageForMiraDoc() + { + if (!string.IsNullOrEmpty(Path)) return Path; + if (_b64Source != null) return _b64Source; + if (_source == null) throw new ArgumentNullException("Для изображения не задан ни путь, ни поток. Необходимо заполнить как минимум одно из этого, для работы"); + _b64Source = GetImageAsStringBase64(_source); + return _b64Source; + } + + private string GetImageAsStringBase64(Stream stream) + { + //Вот это вот, в общем-то, ещё один жуткий костыль. Дело в том, что MigraDoc не поддерживает создание изображений из потоков. Только из путей. Но всё же, способ есть + //Вот он собственно. И да, фактически это жуткая система, записывающая картинку в строку. Увы, по другому не получится + stream.Position = 0; + int count = (int)stream.Length; + byte[] data = new byte[count]; + stream.Read(data, 0, count); + return $"base64:{Convert.ToBase64String(data)}"; + } + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/PdfImage.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/PdfImage.cs new file mode 100644 index 0000000..60d52a5 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/ImageModels/PdfImage.cs @@ -0,0 +1,41 @@ +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Models.ImageModels +{ + /// + /// Информация о изображении + /// + public class PdfImage : IPdfElement + { + /// + /// Путь до изображения + /// + public MiraDocImage Image { get; set; } = null!; + + /// + /// Ширина изображения + /// + public int? Width { get; set; } = null; + + /// + /// Высота изображения + /// + public int? Height { get; set; } = null; + + /// + /// Выравнивание текста внутри параграфа (по умолчанию - по левой строне) + /// + public PdfAlignmentType ImageAlignment { get; set; } = PdfAlignmentType.Left; + + /// + /// Отступ после параграфа (по умолчанию средний) + /// + public PdfMargin MarginAfter { get; set; } = PdfMargin.Medium; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartData.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartData.cs new file mode 100644 index 0000000..4542f04 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartData.cs @@ -0,0 +1,39 @@ +using System.Drawing; + +namespace PIHelperSh.PdfCreator.Models.PieChartModel +{ + /// + /// Элемент данных. Одно значение для диаграммы. + /// + public class PdfPieChartData + { + /// + /// Название варианта + /// + public string DisplayName { get; set; } = null!; + + /// + /// Значение варианта(по ним строят диаграмму) + /// + public double Value { get; set; } + + /// + /// Цвет области на диаграме. При null будет использоватсся выдача цветов по умолчанию) + /// + public Color? Color { get; set; } = null; + + /// + /// + public PdfPieChartData() { } + + /// + /// + /// + /// + public PdfPieChartData(string displayName, double value) + { + DisplayName = displayName; + Value = value; + } + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartModel.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartModel.cs new file mode 100644 index 0000000..21968a1 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/PieChartModel/PdfPieChartModel.cs @@ -0,0 +1,50 @@ +using PIHelperSh.PdfCreator.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Models.PieChartModel +{ + /// + /// Модель круговой диаграммы + /// + public class PdfPieChartModel + { + /// + /// Ширина диаграммы в см по умолчанию на всю ширину + /// + public double Width { get; set; } = 16; + + /// + /// Высота диаграммы в см + /// + public double Height { get; set; } = 12; + + /// + /// Заголовок диаграммы. Будет отображён над ней + /// + public string ChartName { get; set; } = null!; + + /// + /// Стиль заголовка + /// + public PdfStyleType HeaderStyle { get; set; } = PdfStyleType.Bold; + + /// + /// Выравнивание заголовка по горизонтали + /// + public PdfAlignmentType HeaderAlignment { get; set; } = PdfAlignmentType.Center; + + /// + /// Вариант расположения легенды + /// + public PdfLegendPosition LegendPosition { get; set; } = PdfLegendPosition.Bottom; + + /// + /// Набор данных для создания диаграммы + /// + public List DataSet { get; set; } = new(); + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTable.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTable.cs new file mode 100644 index 0000000..9c93e1b --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTable.cs @@ -0,0 +1,51 @@ +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; + +namespace PIHelperSh.PdfCreator.Models.TableModels +{ + /// + /// Простая табличка в PDF + /// + public class PDFSimpleTable + { + /// + /// Заголовок + /// + public List? Header { get; set; } = null; + + /// + /// Стиль заголовка (по умолчанию - жирный) + /// + public PdfStyleType HeaderStyle { get; set; } = PdfStyleType.Bold; + + /// + /// Выравнивание текста внутри заголовка (по умолчанию - по центру) + /// + public PdfAlignmentType HeaderHorizontalAlignment { get; set; } = PdfAlignmentType.Center; + + /// + /// Набор строк + /// + public List Rows { get; set; } = new(); + + /// + /// Базовый стиль строк + /// + public PdfStyleType RowStyle = PdfStyleType.Basic; + + /// + /// Базовое выравнивание элементов сторок + /// + public PdfAlignmentType RowHorizontalAlignment = PdfAlignmentType.Rigth; + + /// + /// Отступ после таблицы + /// + public PdfMargin MarginAfter = PdfMargin.None; + + /// + /// Меняем ли ориентацию страницы на альбомную + /// + public bool ChangePageOrientation = false; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTableRow.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTableRow.cs new file mode 100644 index 0000000..8b7e30a --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PDFSimpleTableRow.cs @@ -0,0 +1,30 @@ +using PIHelperSh.PdfCreator.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Models.TableModels +{ + /// + /// Строка простой таблицы + /// + public class PDFSimpleTableRow + { + /// + /// Элемменты данной стоки + /// + public List Items = new List(); + + /// + /// Стиль элементов строки, если он отличается от стиля строк, определённого в таблице + /// + public PdfStyleType? Style = null; + + /// + /// Выравнивание элементов строки, если оно отличается от выравнивания, определённого в таблице + /// + public PdfAlignmentType? Alignment = null; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTable.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTable.cs new file mode 100644 index 0000000..81b483b --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTable.cs @@ -0,0 +1,42 @@ +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; + +namespace PIHelperSh.PdfCreator.Models.TableModels +{ + /// + /// Таблица для вставки на лист + /// + /// + public class PdfTable + { + /// + /// Шапка таблицы(2 строки/2 столбца) + /// + public List? Header { get; set; } = null; + + /// + /// Стиль заголовка (по умолчанию - жирный) + /// + public PdfStyleType HeaderStyle { get; set; } = PdfStyleType.Bold; + + /// + /// Выравнивание текста внутри заголовка (по умолчанию - по центру) + /// + public PdfAlignmentType HeaderHorizontalAlignment { get; set; } = PdfAlignmentType.Center; + + /// + /// Список объектов, информация о которых будет в таблице. + /// + public List Records { get; set; } = new(); + + /// + /// Стиль объектов в таблице (по умолчанию - базовый) + /// + public PdfStyleType RecordStyle { get; set; } = PdfStyleType.Basic; + + /// + /// Выравнивание текста объектов в таблице (по умолчанию - по левой строне) + /// + public PdfAlignmentType RecordHorizontalAlignment { get; set; } = PdfAlignmentType.Left; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumn.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumn.cs new file mode 100644 index 0000000..f436fdd --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumn.cs @@ -0,0 +1,25 @@ +using PIHelperSh.PdfCreator.Interfaces; + +namespace PIHelperSh.PdfCreator.Models.TableModels +{ + /// + /// Столбец(строка) таблицы, определяющий дальнейшее содержимое + /// + public class PdfTableColumn : IPdfColumnItem + { + /// + /// Заголовок + /// + public string? Title { get; set; } + + /// + /// Размер (Ширина столбца/Высота строки) + /// + public float? Size { get; set; } + + /// + /// Название свойства, которое будет заполнять столбец + /// + public string PropertyName { get; set; } = null!; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumnGroup.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumnGroup.cs new file mode 100644 index 0000000..51db426 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TableModels/PdfTableColumnGroup.cs @@ -0,0 +1,25 @@ +using PIHelperSh.PdfCreator.Interfaces; + +namespace PIHelperSh.PdfCreator.Models.TableModels +{ + /// + /// Группа столбцов(строк) таблицы, определяющий дальнейшее содержимое + /// + public class PdfTableColumnGroup : IPdfColumnItem + { + /// + /// Заголовок + /// + public string? Title { get; set; } + + /// + /// Количество элементов в группе + /// + public float? Size => InnerColumns.Sum(x => x.Size); + + /// + /// Столбцы/строки в группе + /// + public List InnerColumns { get; set; } = new(); + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfList.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfList.cs new file mode 100644 index 0000000..17b210e --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfList.cs @@ -0,0 +1,26 @@ +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Models.TextModels +{ + /// + /// Список элементов в PDF + /// + public class PdfList : IPdfElement + { + /// + /// Элементы списка (параграфы или иные спсики) + /// + public List Content { get; set; } = new(); + + /// + /// Отступ после списка (по умолчанию - средний) + /// + public PdfMargin MarginAfter { get; set; } = PdfMargin.Medium; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfParagraph.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfParagraph.cs new file mode 100644 index 0000000..60282bd --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/Models/TextModels/PdfParagraph.cs @@ -0,0 +1,36 @@ +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PIHelperSh.PdfCreator.Models.TextModels +{ + /// + /// Параграф в PDF + /// + public class PdfParagraph : IPdfElement + { + /// + /// Текст параграфа + /// + public string Text { get; set; } = string.Empty; + + /// + /// Стиль параграфа (по умолчанию - базовый) + /// + public PdfStyleType Style { get; set; } = PdfStyleType.Basic; + + /// + /// Выравнивание текста внутри параграфа (по умолчанию - по левой строне) + /// + public PdfAlignmentType ParagraphAlignment { get; set; } = PdfAlignmentType.Left; + + /// + /// Отступ после параграфа (по умолчанию средний) + /// + public PdfMargin MarginAfter { get; set; } = PdfMargin.Medium; + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PIHelperSh.PdfCreator.csproj b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PIHelperSh.PdfCreator.csproj new file mode 100644 index 0000000..ebb8d93 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PIHelperSh.PdfCreator.csproj @@ -0,0 +1,38 @@ + + + + net8.0 + enable + enable + True + Библиотека PDF + MaximK + Небольшая надстройка для более удобной работы с PDF + README.md + https://github.com/KuzarinM/PIHelperSh/tree/master/PIHelperSh.PdfCreater + True + LICENSE + 1.1.1 + True + + + + + True + \ + + + + + + + + + + + True + \ + + + + diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PdfCreator.cs b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PdfCreator.cs new file mode 100644 index 0000000..ca7eb08 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/PdfCreator.cs @@ -0,0 +1,550 @@ +using MigraDoc.DocumentObjectModel; +using System.Text; +using MigraDoc.Rendering; +using MigraDoc.DocumentObjectModel.Tables; +using System.Reflection; +using MigraDoc.DocumentObjectModel.Shapes.Charts; +using PIHelperSh.Core.Extensions; +using PIHelperSh.PdfCreator.Enums; +using PIHelperSh.PdfCreator.Models.TableModels; +using PIHelperSh.PdfCreator.Models.TextModels; +using PIHelperSh.PdfCreator.Models.ImageModels; +using PIHelperSh.PdfCreator.Models.PieChartModel; +using PIHelperSh.PdfCreator.Interfaces; + +namespace PIHelperSh.PdfCreator +{ + /// + /// + public class PdfCreator : IPdfCreator + { + private Document? _document; + private Section? _section; + + private readonly Unit _borderWidth = 0.5; + + /// + /// + public PdfCreator() + { + _document = new Document(); + DefineStyles(_document); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + _section = _document.AddSection(); + } + + #region Внутренние методы + private static string GetListLavel(int level) + { + try + { + return level.CreateEnumFromValue().GetValue(); + } + catch (Exception) + { + return PdfStyleType.Basic.GetValue(); + } + } + + private void ConfigurateChartLegend(Chart chart, PdfLegendPosition position) + { + switch (position) + { + case PdfLegendPosition.Bottom: + chart.FooterArea.AddLegend(); + break; + case PdfLegendPosition.Right: + chart.RightArea.AddLegend(); + break; + case PdfLegendPosition.Left: + chart.LeftArea.AddLegend(); + break; + case PdfLegendPosition.Top: + chart.TopArea.AddLegend(); + break; + } + } + + private static void DefineStyles(Document document) + { + #region Базовый стиль + var style = document.Styles["Normal"]; + style.Font.Name = "Times New Roman"; + style.Font.Size = 14; + #endregion + + #region Стиль заголовка + style = document.Styles.AddStyle("NormalTitle", "Normal"); + style.Font.Bold = true; + style.Font.Size = 18; + #endregion + + #region Стиль жирный + style = document.Styles.AddStyle("NormalBold", "Normal"); + style.Font.Bold = true; + #endregion + + #region Маркированный список уровень 1 + style = document.AddStyle("BulletList", "Normal"); + style.ParagraphFormat.LeftIndent = "1.5cm"; + style.ParagraphFormat.ListInfo = new ListInfo + { + ContinuePreviousList = true,//Продолжать список, который уже был ранее + ListType = ListType.BulletList1//Маркер + }; + style.ParagraphFormat.TabStops.ClearAll(); + style.ParagraphFormat.TabStops.AddTabStop(Unit.FromCentimeter(1.5), TabAlignment.Left); + + style.ParagraphFormat.FirstLineIndent = "-0.5cm"; + #endregion + + #region Маркированный список уровень 2 + style = document.AddStyle("BulletList2", "BulletList"); + style.ParagraphFormat.LeftIndent = "3.0cm"; + style.ParagraphFormat.ListInfo.ListType = ListType.BulletList2; + style.ParagraphFormat.TabStops.ClearAll(); + style.ParagraphFormat.TabStops.AddTabStop(Unit.FromCentimeter(3.0), TabAlignment.Left); + #endregion + + #region Маркированный список уровень 3 + style = document.AddStyle("BulletList3", "BulletList"); + style.ParagraphFormat.LeftIndent = "4.5cm"; + style.ParagraphFormat.ListInfo.ListType = ListType.BulletList3; + style.ParagraphFormat.TabStops.ClearAll(); + style.ParagraphFormat.TabStops.AddTabStop(Unit.FromCentimeter(4.5), TabAlignment.Left); + #endregion + + #region Мелкий шрифт + style = document.Styles.AddStyle("NormalSmall", "Normal"); + style.Font.Size = 11; + #endregion + } + + private Paragraph? MakeParagraph(PdfParagraph pdfParagraph) + { + if (_section == null) + return null; + var paragraph = _section.AddParagraph(pdfParagraph.Text); + paragraph.Format.Alignment = pdfParagraph.ParagraphAlignment.GetValue(); + //Т.к стили могут назначаться по иному, тут будет вот так вот + return paragraph; + } + + private Paragraph? MakeList(PdfList pdfList, int level) + { + Paragraph? last = null; + foreach (IPdfElement element in pdfList.Content) + { + if (element is PdfParagraph par) + { + var paragraph = MakeParagraph(par); + if (paragraph == null) + continue; + + paragraph.Format.SpaceAfter = "0.3cm"; + paragraph.Style = GetListLavel(level); + last = paragraph; + } + else if (element is PdfList ls) + { + last = MakeList(ls, level + 1); + } + } + return last; + } + + private void ConfigurateParagraph(Paragraph paragraph, PdfParagraph properties) + { + paragraph.Format.Alignment = properties.ParagraphAlignment.GetValue(); ; + if (properties.MarginAfter != PdfMargin.None) paragraph.Format.SpaceAfter = properties.MarginAfter.GetValue(); + paragraph.Style = properties.Style.GetValue(); + } + + private void ConfigurateCell(Cell cell, string text, PdfAlignmentType alignment, PdfStyleType style, int? rightMerge = null, int? downMerge = null, bool dcw = false) + { + if (rightMerge.HasValue) cell.MergeRight = rightMerge.Value; + if (downMerge.HasValue) + { + cell.MergeDown = downMerge.Value; + cell.VerticalAlignment = VerticalAlignment.Center; + } + + Paragraph paragraph = cell.AddParagraph(text); + ConfigurateParagraph(paragraph, new() + { + ParagraphAlignment = alignment, + Style = style, + MarginAfter = PdfMargin.None + }); + + cell.Borders.Left.Width = _borderWidth; + cell.Borders.Right.Width = _borderWidth; + cell.Borders.Top.Width = _borderWidth; + cell.Borders.Bottom.Width = _borderWidth; + + if (dcw) + { + float columnNeeds = text.Length / 3.8f; + if (cell.Column.Width.Centimeter < columnNeeds) + cell.Column.Width = Unit.FromCentimeter(columnNeeds); + } + } + + private Func GetGetter(FieldInfo[] fields, PropertyInfo[] props, string Name) + { + var a = fields.FirstOrDefault(x => x.Name == Name); + if (a != null) return a.GetValue; + + var b = props.FirstOrDefault(x => x.Name == Name); + if (b != null) return b.GetValue; + + throw new KeyNotFoundException($"У объекта не найдено поле/свойство {Name}"); + } + + private List> MakeTableHeader(Table table, PdfTable header) + { + foreach (var item in header.Header!) + { + if (item is PdfTableColumnGroup group) + { + group.InnerColumns.ForEach(x => table.AddColumn(Unit.FromCentimeter(x.Size!.Value))); + } + else + { + table.AddColumn(Unit.FromCentimeter(item.Size!.Value)); + } + } + + Row upRow = table.AddRow(); + Row downRow = table.AddRow(); + upRow.HeadingFormat = downRow.HeadingFormat = true; + upRow.Format.Font.Bold = downRow.Format.Font.Bold = true; + + int upColumn = 0; + int downColumn = 0; + + Type eType = typeof(T); + + var fields = eType.GetFields(); + var prop = eType.GetProperties(); + + List> objectFields = new(); + + foreach (var item in header.Header) + { + if (item is PdfTableColumnGroup group) + { + ConfigurateCell(upRow.Cells[upColumn], group.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, rightMerge: group.InnerColumns.Count - 1); + upColumn += group.InnerColumns.Count; + foreach (var ic in group.InnerColumns) + { + ConfigurateCell(downRow.Cells[downColumn], ic.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle); + objectFields.Add(GetGetter(fields, prop, ic.PropertyName)); + downColumn++; + } + } + else + { + ConfigurateCell(upRow.Cells[upColumn], item.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, downMerge: 1); + ConfigurateCell(downRow.Cells[downColumn], item.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle); + objectFields.Add(GetGetter(fields, prop, ((PdfTableColumn)item).PropertyName)); + upColumn++; + downColumn++; + } + } + + return objectFields; + } + + private void MakeSimpleTableHeader(Table table, PDFSimpleTable header) + { + foreach (var item in header.Header!) + { + if (item is PdfTableColumnGroup group) + { + group.InnerColumns.ForEach(x => table.AddColumn(Unit.FromCentimeter(x.Size!.Value))); + } + else + { + table.AddColumn(Unit.FromCentimeter(item.Size!.Value)); + } + } + + Row upRow = table.AddRow(); + Row downRow = table.AddRow(); + upRow.HeadingFormat = downRow.HeadingFormat = true; + upRow.Format.Font.Bold = downRow.Format.Font.Bold = true; + + int upColumn = 0; + int downColumn = 0; + + foreach (var item in header.Header) + { + if (item is PdfTableColumnGroup group) + { + ConfigurateCell(upRow.Cells[upColumn], group.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, rightMerge: group.InnerColumns.Count - 1); + upColumn += group.InnerColumns.Count; + foreach (var ic in group.InnerColumns) + { + ConfigurateCell(downRow.Cells[downColumn], ic.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle); + downColumn++; + } + } + else + { + ConfigurateCell(upRow.Cells[upColumn], item.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, downMerge: 1); + ConfigurateCell(downRow.Cells[downColumn], item.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle); + upColumn++; + downColumn++; + } + } + } + + private void MakeTableWithHederInRow(Table table, PdfTable header) + { + for (int i = 0; i < header.Records.Count + 2; i++) + { + _ = table.AddColumn("0.5cm"); + } + + Type tp = typeof(T); + var fields = tp.GetFields(); + var props = tp.GetProperties(); + + foreach (var item in header.Header!) + { + Row? row = table.AddRow(); + if (item is PdfTableColumnGroup group) + { + ConfigurateCell(row.Cells[0], group.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, downMerge: group.InnerColumns.Count - 1, dcw: true); + foreach (var innerParam in group.InnerColumns) + { + row ??= table.AddRow();//Первая(а точнее вторая) часть костыля с первой строкой + row.HeightRule = RowHeightRule.Exactly; + row.Height = Unit.FromCentimeter(innerParam.Size!.Value);//Устанавливаем высоту строки. Так просили... + + ConfigurateCell(row.Cells[1], innerParam.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, dcw: true); + + Func getter = GetGetter(fields, props, innerParam.PropertyName); + for (int i = 0; i < header.Records.Count; i++) + { + ConfigurateCell(row.Cells[i + 2], getter(header.Records[i])!.ToString()!, header.RecordHorizontalAlignment, header.RecordStyle, dcw: true); + } + row = null; //Это небольшой костыль, который позволяет не создавать новую строчку в самый первый раз(т.к. она же есть) + } + } + else if (item is PdfTableColumn column) + { + row.HeightRule = RowHeightRule.Exactly; + row.Height = Unit.FromCentimeter(column.Size!.Value); + ConfigurateCell(row.Cells[0], column.Title!, header.HeaderHorizontalAlignment, header.HeaderStyle, rightMerge: 1, dcw: true); + + Func getter = GetGetter(fields, props, column.PropertyName); + for (int i = 0; i < header.Records.Count; i++) + { + ConfigurateCell(row.Cells[i + 2], getter(header.Records[i])!.ToString()!, header.RecordHorizontalAlignment, header.RecordStyle, dcw: true); + } + } + } + } + + private void ConfiguratePieChart(Chart chart, PdfPieChartModel pieChart) + { + chart.Width = Unit.FromCentimeter(pieChart.Width); + chart.Height = Unit.FromCentimeter(pieChart.Height); + + Paragraph p = chart.HeaderArea.AddParagraph(pieChart.ChartName); + ConfigurateParagraph(p, new() + { + Style = pieChart.HeaderStyle, + ParagraphAlignment = pieChart.HeaderAlignment, + MarginAfter = PdfMargin.None + }); + + chart.LineFormat.Visible = true; + ConfigurateChartLegend(chart, pieChart.LegendPosition); + chart.DataLabel.Position = DataLabelPosition.OutsideEnd; + } + + #endregion + + /// + /// Создаём параграф + /// + /// Модель параграфа, который вставляем + public void AddParagraph(PdfParagraph pdfParagraph) + { + var paragraph = MakeParagraph(pdfParagraph); + if (paragraph == null) + return; + + ConfigurateParagraph(paragraph, pdfParagraph); + } + + /// + /// Создаём маркированный список + /// + /// Модель списка(может быть многоуровневым). Max - 3 уровня + public void AddList(PdfList pdfList) + { + if (_section == null) + { + return; + } + var lastP = MakeList(pdfList, 1); + if (lastP != null) lastP.Format.SpaceAfter = pdfList.MarginAfter.GetValue(); + } + + /// + /// Создаёт таблицу, с шапкой из 2-х строк(с группировками) + /// + /// Тип DTO, из которой берутся данные в таблицу + /// Модель самой таблицы + /// + public void AddTable(PdfTable header, bool rowHeaded = false) + { + if (_document == null) + { + return; + } + + if (rowHeaded) + { + _section!.PageSetup.Orientation = Orientation.Landscape; + _section.PageSetup.LeftMargin = 10; + MakeTableWithHederInRow(_document.LastSection.AddTable(), header); + return; + } + + var table = _document.LastSection.AddTable(); + if (rowHeaded) + { + MakeTableWithHederInRow(table, header); + return; + } + + var maper = MakeTableHeader(table, header); + + foreach (var item in header.Records) + { + var row = table.AddRow(); + for (int i = 0; i < maper.Count; i++) + { + ConfigurateCell(row.Cells[i], maper[i](item)?.ToString()!, header.RecordHorizontalAlignment, header.RecordStyle); + } + } + } + + /// + /// Создаёт табличку, наподобие той, что с T, но проще + /// + /// + public void AddSimpleTable(PDFSimpleTable tableData) + { + if (_document == null) + { + return; + } + + if (tableData.ChangePageOrientation) + { + _section!.PageSetup.Orientation = Orientation.Landscape; + _section.PageSetup.LeftMargin = 10; + _section.PageSetup.BottomMargin = 5; + } + + var table = _document.LastSection.AddTable(); + + MakeSimpleTableHeader(table, tableData); + + foreach (var item in tableData.Rows) + { + var row = table.AddRow(); + + for (int i = 0; i < item.Items.Count; i++) + { + ConfigurateCell(row.Cells[i], item.Items[i], item.Alignment ?? tableData.RowHorizontalAlignment, item.Style ?? tableData.RowStyle); + } + } + if (tableData.MarginAfter != PdfMargin.None) table.Format.SpaceAfter = tableData.MarginAfter.GetValue(); + } + + /// + /// Создаёт круговую диаграмму. + /// + /// Модель для круговой диаграммы + public void AddChart(PdfPieChartModel pieChart) + { + if (_document == null) + { + return; + } + + Chart chart = new Chart(ChartType.Pie2D); + ConfiguratePieChart(chart, pieChart); + + Series series = chart.SeriesCollection.AddSeries(); + XSeries xseries = chart.XValues.AddXSeries(); + + foreach (var item in pieChart.DataSet) + { + var p = series.Add(item.Value); + xseries.Add(item.DisplayName); + if (item.Color.HasValue) + { + p.FillFormat.Color = new Color((uint)item.Color.Value.ToArgb()); + } + } + + _section?.Add(chart); + } + + /// + /// Добавляем на лист изображение. Можно по пути, можно по потоку, можно по Base64 строке + /// + /// Модель одного изображения + public void AddImage(PdfImage img) + { + if (_section == null) return; + var paragraph = _section.AddParagraph();//Это некоторый костыли. у изображения настроить выравнивание - это очень непросто. А вот так вот - можно + var image = paragraph.AddImage(img.Image.GetImageForMiraDoc()); + + if (img.Width.HasValue) image.Width = img.Width.Value; + if (img.Height.HasValue) image.Width = img.Height.Value; + + paragraph.Format.Alignment = img.ImageAlignment.GetValue(); + paragraph.Format.SpaceAfter = img.MarginAfter.GetValue(); + } + + /// + /// Метод сохранения созданного PDF документа + /// + /// Поток MemoryStream с документом + public MemoryStream SavePdf() + { + var renderer = new PdfDocumentRenderer(true) + { + Document = _document + }; + renderer.RenderDocument(); + + MemoryStream file = new MemoryStream(); + renderer.PdfDocument.Save(file, false); + + file.Seek(0, SeekOrigin.Begin); + return file; + } + + /// + /// Метод сохранения созданного PDF документа в файл + /// + /// Имя файла и путь до него. Проверки на расширение нет + public void SavePdf(string filename) + { + using Stream streamToWriteTo = File.Open(filename, FileMode.Create); + MemoryStream ms = SavePdf(); + ms.Position = 0; + ms.CopyTo(streamToWriteTo); + } + } +} diff --git a/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/README.md b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/README.md new file mode 100644 index 0000000..c300619 --- /dev/null +++ b/Cop.Borovkov.Var3/PIHelperSh.PdfCreater/README.md @@ -0,0 +1,87 @@ +# Библиотека PDF +Данная библиотека является надстройкой над MigraDoc и упрощает работу с PDF в контексте ряда операций. +Основным интерфейсом системы является **IPdfCreator**. На данный момент имеется единственная реализация - **PdfCreator** +## Основные методы IPdfCreator + - **AddParagraph(PdfParagraph paragraph)** данный метод предназначен для добавления на лист PDF параграфа текста. Принимает стандартную модель параграфа (о ней позже) + - **AddList(PdfList List)** данный метод предназначен для добавления на лист PDF маркированного списка. Принимает стандартныую модель списка ( о ней позже). `Поддерживается не более 3-х уровней!` + - **AddChart(PdfPieChartModel pieChart)** данный метод предназначен для добавления на лист PDF круговой диаграммы. Принимает модель диаграммы (о ней позже) + - **AddImage(PdfImage image)** данный метод предназначен для добавления на лист изображения. Принимает стандартную модель (о ней позже). + - **MemoryStream SavePdf()** данный метод сохраняет PDF документ и возвращает поток данных файла. + - **SavePdf(string filename)** данный метод сохраняет PDF документ на диске по пути из filename + - **AddTable\(PdfTable\ header, bool rowHeaded = false)** данный метод позволяет добавлять на лист таблицу с шапкой и значениями. **rowHeaded** позволяет описать будет ли шапка в строках или в столбцах. + - **AddSimpleTable(PDFSimpleTable tableData)** данный метод позволяет добавть на лист простую табличку, строки в которой задаются пользователем самостоятельно. + ## Основные модели + ### PdfParagraph + Модель имеет следующие поля: + - **PdfMargin MarginAfter** - Отступ после параграфа. По умолчанию значение *Medium* + - **PdfAlignmentType ParagraphAlignment** - Выравнивание параграфа. По умолчанию значение *Left* + - **PdfStyleType Style** - Стиль параграфа. По сути объединение шрифта, жирности и отступов. По умолчанию - Basic. На данный момент имеются следующие (В дальнейшем планируется добавить возможность добавлять свои стили): + - *Basic*: Times New Roman 14 + - *Title*: Times New Roman 18 жирный + - *Bold*: Times New Roman 14 жирный + - *ListLevel1*: Times New Roman 14 отступ 1.5cm + - *ListLevel2*: Times New Roman 14 отступ 3.0cm + - *ListLevel3*: Times New Roman 14 отступ 4.5cm + - *Small*: Times New Roman 11 + - **string Text** - Текст параграфа +### PdfList +Данная модель позволяет создать многоуровневый список. + - **PdfMargin MarginAfter** - подробно описан ранее + - **List\ Content** - элементы списка. Это могут быть как параграфы, так и другие списки +### PdfPieChartModel +Данная модель позволяет добавлять диаграмму на лист + - **double Width** - Ширина диаграммы на листе. По умолчанию 16. + - **double Height** - Высота диаграммы на листе. По умолчанию 12. + - **string ChartName** - Надпись над диаграммой. По сути её имя. + - **PdfStyleType HeaderStyle** - Стиль заголовка диаграммы. + - **PdfAlignmentType HeaderAlignment** - Выравнивание заголовка диаграммы. + - **PdfLegendPosition LegendPosition** - Местоположение легенды диаграммы. + - **List\ DataSet** - Набор данных для диаграммы +### PdfPieChartData: + - **string DisplayName** - Имя для значения + - **double Value** - Значение + - **Color? Color** - Если нужно, цвет для значения +### PdfImage +Моделька для вывода изображения на лист. Имеет поля: + - **MigradocImage Image** - само изображение (обёртка) + - **int? Width** - Ширина изображения (если null - по размеру изображения) + - **int? Height** - Высота сообщения (если null - по соотношению сторон к длине) + - **PdfAlignmentType ImageAlignment** - Выравнивание изображения (по умолчанию - Left) + - **PdfMargin MarginAfter** - Отступ после. По умолчанию Medium +### MigradocImage +Обёртка, предназначенная для вывода изображений в MigraDock + Объект создаётся только с помощью статических методов: + - **CreateFromPath(string path)** - создаём из картинки, расположенной на компьютере по пути + - **MigradocImage CreateFromBase64(string base64)** - создаём из base64 строки, в которой зашифрована картинка + - **MigradocImage CreateFromStream(Stream stream)** - создаём из потока картинки +### PdfTable +Сейчас будет немного сложно. Данная модель предназначена для создания таблицы из коллекции объектов определённого типа, при этом шапка таблицы поддерживает 2 уровня(то есть, ряд столбцов могут быть сгруппированы общим заголовком). Поля: + - **List\? Header** - модельки заголовков таблицы + - **PdfStyleType HeaderStyle** - Стиль заголовка таблицы. По умолчанию - Bold + - **PdfAlignmentType HeaderHorisontalAlignment** - Выравнивание текста внутри заголовков. По умолчанию - по центру + - **PdfStyleType RecordStyle** - Стиль текста записей строк таблицы. По умолчанию - Basic + - **PdfAlignmentType RecordHorisontalAlignment** - Выравнивае записей строк таблицы внутри ячейки. По умолчанию - Left + - **List\ Records** - список записей, из которых формируем таблицу + ### PdfTableColumn + Столбец таблицы: + - **string? Title** - заголовок + - **float? Size** - ширина/высота ячейки + - **string PropertyName** - Имя свойства, которое будет заполнять данный столбец/строку + ### PdfTableColumnGroup + Сгруппированные столбцы/строки заголовка + Поля: + - **string? Title** - заголовок этой группы + - **List\ InnerColumns** - внутренние столбцы/строки + ### PDFSimpleTable + - **List? Header** - Заголовок таблицы. Уже был ранее описан + - **PdfStyleType HeaderStyle** - Стиль заголовка таблицы. По умолчанию - Bold + - **PdfAlignmentType HeaderHorisontalAlignment** - Выравнивание текста внутри заголовков. По умолчанию - по центру + - **List Rows** - непосредственно строки таблицы + - **PdfStyleType RowStyle** - ситль текста таблицы по умолчанию. Может быть переопределён в каждой строке + - **PdfAlignmentType RowHorisontalAlignment** - выравнивание текста строки таблицы по умолчанию. Может быть переопределён + - **PdfMargin MarginAfter** - Отступ после. По умолчанию None + - **bool ChangePageOrientation** - флаг, меняем ли мы ориентацию листа на альбомную +### PDFSimpleTableRow + - **List** Items - элементы строки таблицы + - **PdfStyleType? Style** - возможность переопределить стиль данной строки. Если Null - берётся стиль по умолчанию + - **PdfAlignmentType? Alignment** - возможность переопределить выравниваение данной строки. Если Null - берётся стиль по умолчанию \ No newline at end of file diff --git a/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.Designer.cs b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.Designer.cs new file mode 100644 index 0000000..28464d4 --- /dev/null +++ b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.Designer.cs @@ -0,0 +1,62 @@ +namespace TestCustomComponents.Forms +{ + partial class Form2 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + customPdfTable1 = new Cop.Borovkov.Var3.Components.CustomPdfTable(components); + button1 = new Button(); + SuspendLayout(); + // + // button1 + // + button1.Location = new Point(12, 12); + button1.Name = "button1"; + button1.Size = new Size(75, 23); + button1.TabIndex = 0; + button1.Text = "button1"; + button1.UseVisualStyleBackColor = true; + button1.Click += button1_Click; + // + // Form2 + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Controls.Add(button1); + Name = "Form2"; + Text = "Form2"; + ResumeLayout(false); + } + + #endregion + + private Cop.Borovkov.Var3.Components.CustomPdfTable customPdfTable1; + private Button button1; + } +} \ No newline at end of file diff --git a/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.cs b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.cs new file mode 100644 index 0000000..9787dc0 --- /dev/null +++ b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.cs @@ -0,0 +1,42 @@ +namespace TestCustomComponents.Forms +{ + public partial class Form2 : Form + { + public Form2() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + customPdfTable1.SaveToPdf(new() + { + FilePath = @"F:\test\test.pdf", + Title = "Текст заголовка", + Tables = [new[,] + { + { "00", "01", "02" }, + { "10", "11", "12" }, + { "20", "21", "22" }, + }], + }); + + customPdfTable1.SaveToPdf(new() + { + FilePath = @"F:\test\test.pdf", + Title = "Текст заголовка", + Tables = [new[,] + { + { "000", "001", "002" }, + { "010", "011", "012" }, + { "020", "021", "022" }, + }, new[,] + { + { "100", "101", "102" }, + { "110", "111", "112" }, + { "120", "121", "122" }, + }], + }); + } + } +} diff --git a/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.resx b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.resx new file mode 100644 index 0000000..b5ff85f --- /dev/null +++ b/Cop.Borovkov.Var3/TestCustomComponents/Forms/Form2.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Cop.Borovkov.Var3/TestCustomComponents/Models/TestModel.cs b/Cop.Borovkov.Var3/TestCustomComponents/Models/TestModel.cs index 053c6d2..58922d9 100644 --- a/Cop.Borovkov.Var3/TestCustomComponents/Models/TestModel.cs +++ b/Cop.Borovkov.Var3/TestCustomComponents/Models/TestModel.cs @@ -4,7 +4,7 @@ { public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public int Age { get; set; } } diff --git a/Cop.Borovkov.Var3/TestCustomComponents/Program.cs b/Cop.Borovkov.Var3/TestCustomComponents/Program.cs index bf2c653..13d3c8d 100644 --- a/Cop.Borovkov.Var3/TestCustomComponents/Program.cs +++ b/Cop.Borovkov.Var3/TestCustomComponents/Program.cs @@ -1,3 +1,5 @@ +using TestCustomComponents.Forms; + namespace TestCustomComponents { internal static class Program @@ -11,7 +13,7 @@ namespace TestCustomComponents // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); - Application.Run(new Form1()); + Application.Run(new Form2()); } } } \ No newline at end of file