Интеграция таблицы с источниками данных PDF

Следующий фрагмент кода также работает с библиотекой Aspose.PDF.Drawing.

Интеграция таблицы с базой данных

Базы данных созданы для хранения и управления данными. Распространенной практикой среди программистов является заполнение различных объектов данными из баз данных. В этой статье обсуждается добавление данных из базы данных в таблицу. Возможно заполнить объект Table данными из любого источника данных с помощью Aspose.PDF для .NET. И это не только возможно, но и очень просто.

Aspose.PDF для .NET позволяет разработчикам импортировать данные из:

  • Массива объектов
  • DataTable
  • DataView

Эта тема предоставляет информацию о получении данных из DataTable или DataView.

Все разработчики, работающие на платформе .NET, должны быть знакомы с основными концепциями ADO.NET, введенными .NET Framework. Все разработчики, работающие на платформе .NET, должны быть знакомы с базовыми концепциями ADO.NET, введенными .NET Framework.

Методы ImportDataTable(..) и ImportDataView(..) класса Table используются для импорта данных из баз данных.

Пример ниже демонстрирует использование метода ImportDataTable. В этом примере объект DataTable создается с нуля, и записи добавляются программно, вместо того чтобы заполнять DataTable данными из баз данных. Разработчики также могут заполнять DataTable из базы данных по своему желанию.

// Для полных примеров и файлов данных, пожалуйста, перейдите по ссылке https://github.com/aspose-pdf/Aspose.PDF-for-.NET
// Путь к директории документов.
string dataDir = RunExamples.GetDataDir_AsposePdf_Tables();

DataTable dt = new DataTable("Employee");
dt.Columns.Add("Employee_ID", typeof(Int32));
dt.Columns.Add("Employee_Name", typeof(string));
dt.Columns.Add("Gender", typeof(string));
// Добавляем 2 строки в объект DataTable программно
DataRow dr = dt.NewRow();
dr[0] = 1;
dr[1] = "John Smith";
dr[2] = "Male";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr[0] = 2;
dr[1] = "Mary Miller";
dr[2] = "Female";
dt.Rows.Add(dr);
// Создаем экземпляр документа
Document doc = new Document();
doc.Pages.Add();
// Инициализируем новый экземпляр таблицы
Aspose.Pdf.Table table = new Aspose.Pdf.Table();
// Устанавливаем ширину столбцов таблицы
table.ColumnWidths = "40 100 100 100";
// Устанавливаем цвет границы таблицы как LightGray
table.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Устанавливаем границы для ячеек таблицы
table.DefaultCellBorder = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
table.ImportDataTable(dt, true, 0, 1, 3, 3);

// Добавляем объект таблицы на первую страницу входного документа
doc.Pages[1].Paragraphs.Add(table);
dataDir = dataDir + "DataIntegrated_out.pdf";
// Сохраняем обновленный документ, содержащий объект таблицы
doc.Save(dataDir);

Как определить, разорвется ли таблица на текущей странице

Таблицы по умолчанию добавляются с верхней левой позиции, и если таблица достигает конца страницы, она автоматически разрывается. Вы можете программно получить информацию о том, поместится ли таблица на текущей странице или разорвется внизу страницы. Для этого сначала вам нужно получить информацию о размере документа, затем вам нужно получить информацию о верхнем и нижнем полях страницы, информацию о верхнем поле таблицы и высоте таблицы. Если вы добавите верхнее поле страницы + нижнее поле страницы + верхнее поле таблицы + высоту таблицы и вычтете это из высоты документа, вы можете получить количество оставшегося пространства над документом. В зависимости от конкретной высоты строки (которую вы указали), вы можете рассчитать, могут ли все строки таблицы разместиться в оставшемся пространстве над страницей или нет. Пожалуйста, посмотрите следующий фрагмент кода. В следующем коде средняя высота строки составляет 23.002 пункта.

// Для полных примеров и файлов данных, пожалуйста, перейдите на https://github.com/aspose-pdf/Aspose.PDF-for-.NET
// Путь к директории документов.
string dataDir = RunExamples.GetDataDir_AsposePdf_Tables();

// Создайте объект класса PDF
Document pdf = new Document();
// Добавьте раздел в коллекцию разделов PDF документа
Aspose.Pdf.Page page = pdf.Pages.Add();
// Создайте объект таблицы
Aspose.Pdf.Table table1 = new Aspose.Pdf.Table();
table1.Margin.Top = 300;
// Добавьте таблицу в коллекцию параграфов нужного раздела
page.Paragraphs.Add(table1);
// Установите ширину колонок таблицы
table1.ColumnWidths = "100 100 100";
// Установите границу ячейки по умолчанию, используя объект BorderInfo
table1.DefaultCellBorder = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, 0.1F);
// Установите границу таблицы, используя другой настроенный объект BorderInfo
table1.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, 1F);
// Создайте объект MarginInfo и установите его левые, нижние, правые и верхние поля
Aspose.Pdf.MarginInfo margin = new Aspose.Pdf.MarginInfo();
margin.Top = 5f;
margin.Left = 5f;
margin.Right = 5f;
margin.Bottom = 5f;
// Установите отступы ячеек по умолчанию для объекта MarginInfo
table1.DefaultCellPadding = margin;
// Если увеличить счетчик до 17, таблица разорвется
// Потому что она больше не поместится на этой странице
for (int RowCounter = 0; RowCounter <= 16; RowCounter++)
{
    // Создайте строки в таблице, а затем ячейки в строках
    Aspose.Pdf.Row row1 = table1.Rows.Add();
    row1.Cells.Add("col " + RowCounter.ToString() + ", 1");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 2");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 3");
}
// Получите информацию о высоте страницы
float PageHeight = (float)pdf.PageInfo.Height;
// Получите общую информацию о высоте верхнего и нижнего поля страницы,
// верхнем поле таблицы и высоте таблицы.
float TotalObjectsHeight = (float)page.PageInfo.Margin.Top + (float)page.PageInfo.Margin.Bottom + (float)table1.Margin.Top + (float)table1.GetHeight();

// Отобразите информацию о высоте страницы, высоте таблицы, верхнем поле таблицы и верхнем
// и нижнем поле страницы
Console.WriteLine("Высота документа PDF = " + pdf.PageInfo.Height.ToString() + "\nИнформация о верхнем поле = " + page.PageInfo.Margin.Top.ToString() + "\nИнформация о нижнем поле = " + page.PageInfo.Margin.Bottom.ToString() + "\n\nИнформация о верхнем поле таблицы = " + table1.Margin.Top.ToString() + "\nСредняя высота строки = " + table1.Rows[0].MinRowHeight.ToString() + " \nВысота таблицы " + table1.GetHeight().ToString() + "\n ----------------------------------------" + "\nОбщая высота страницы =" + PageHeight.ToString() + "\nСуммарная высота включая таблицу =" + TotalObjectsHeight.ToString());

// Проверьте, если вычесть сумму верхнего поля страницы + нижнего поля страницы
// + верхнего поля таблицы и высоты таблицы из высоты страницы, и это меньше
// чем 10 (средняя высота строки может быть больше 10)
if ((PageHeight - TotalObjectsHeight) <= 10)
    // Если значение меньше 10, то отобразите сообщение.
    // Которое показывает, что другая строка не может быть размещена, и если мы добавим новую
    // строку, таблица разорвется. Это зависит от значения высоты строки.
    Console.WriteLine("Высота страницы - Высота объектов < 10, так что таблица разорвется");

dataDir = dataDir + "DetermineTableBreak_out.pdf";
// Сохраните документ PDF
pdf.Save(dataDir);

Добавление повторяющихся столбцов в таблицу

В классе Aspose.Pdf.Table вы можете установить RepeatingRowsCount, который будет повторять строки, если таблица слишком длинная по вертикали и переходит на следующую страницу. Однако в некоторых случаях таблицы слишком широки, чтобы поместиться на одной странице и должны быть продолжены на следующей странице. Чтобы решить эту задачу, мы реализовали свойство RepeatingColumnsCount в классе Aspose.Pdf.Table. Установка этого свойства приведет к тому, что таблица будет разбиваться на следующую страницу по столбцам и повторять заданное количество столбцов в начале следующей страницы. Следующий фрагмент кода показывает использование свойства RepeatingColumnsCount:

// Для полных примеров и файлов данных, пожалуйста, перейдите по ссылке https://github.com/aspose-pdf/Aspose.PDF-for-.NET
// Путь к директории документов.
string dataDir = RunExamples.GetDataDir_AsposePdf_Tables();

string outFile = dataDir + "AddRepeatingColumn_out.pdf";
// Создание нового документа
Document doc = new Document();
Aspose.Pdf.Page page = doc.Pages.Add();

// Создание внешней таблицы, которая занимает всю страницу
Aspose.Pdf.Table outerTable = new Aspose.Pdf.Table();
outerTable.ColumnWidths = "100%";
outerTable.HorizontalAlignment = HorizontalAlignment.Left;

// Создание объекта таблицы, который будет вложен в outerTable и будет разрываться внутри той же страницы
Aspose.Pdf.Table mytable = new Aspose.Pdf.Table();
mytable.Broken = TableBroken.VerticalInSamePage;
mytable.ColumnAdjustment = ColumnAdjustment.AutoFitToContent;

// Добавление outerTable в абзацы страницы
// Добавление mytable в outerTable
page.Paragraphs.Add(outerTable);
var bodyRow = outerTable.Rows.Add();
var bodyCell = bodyRow.Cells.Add();
bodyCell.Paragraphs.Add(mytable);
mytable.RepeatingColumnsCount = 5;
page.Paragraphs.Add(mytable);

// Добавление заголовочной строки
Aspose.Pdf.Row row = mytable.Rows.Add();
row.Cells.Add("header 1");
row.Cells.Add("header 2");
row.Cells.Add("header 3");
row.Cells.Add("header 4");
row.Cells.Add("header 5");
row.Cells.Add("header 6");
row.Cells.Add("header 7");
row.Cells.Add("header 11");
row.Cells.Add("header 12");
row.Cells.Add("header 13");
row.Cells.Add("header 14");
row.Cells.Add("header 15");
row.Cells.Add("header 16");
row.Cells.Add("header 17");

for (int RowCounter = 0; RowCounter <= 5; RowCounter++)

{
    // Создание строк в таблице, затем ячеек в строках
    Aspose.Pdf.Row row1 = mytable.Rows.Add();
    row1.Cells.Add("col " + RowCounter.ToString() + ", 1");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 2");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 3");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 4");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 5");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 6");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 7");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 11");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 12");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 13");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 14");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 15");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 16");
    row1.Cells.Add("col " + RowCounter.ToString() + ", 17");
}
doc.Save(outFile);

Интеграция таблицы с источником Entity Framework

Более актуально для современного .NET является импорт данных из ORM-фреймворков. В этом случае хорошей идеей будет расширение класса Table методами расширения для импорта данных из простого списка или из группированных данных. Давайте приведем пример для одного из самых популярных ORM - Entity Framework.

public static class PdfHelper
    {
        public static void ImportEntityList<TSource>(this Pdf.Table table, IList<TSource> data)
        {
            var headRow = table.Rows.Add();

            var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var prop in props)
            {
                headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);
            }

            foreach (var item in data)
            {
                // Добавить строку в таблицу
                var row = table.Rows.Add();
                // Добавить ячейки таблицы
                foreach (var t in props)
                {
                    var dataItem = t.GetValue(item, null);
                    if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                        switch (dataType.DataType)
                        {

                            case DataType.Currency:
                                row.Cells.Add(string.Format("{0:C}", dataItem));
                                break;
                            case DataType.Date:
                                var dateTime = (DateTime)dataItem;
                                if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                                {
                                    row.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                        ? dateTime.ToShortDateString()
                                        : string.Format(df.DataFormatString, dateTime));
                                }
                                break;
                            default:
                                row.Cells.Add(dataItem.ToString());
                                break;
                        }
                    else
                    {
                        row.Cells.Add(dataItem.ToString());
                    }
                }
            }
        }
        public static void ImportGroupedData<TKey,TValue>(this Pdf.Table table, IEnumerable<Models.GroupViewModel<TKey, TValue>> groupedData)
        {
            var headRow = table.Rows.Add();           
            var props = typeof(TValue).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var prop in props)
            {
               headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);               
            }

            foreach (var group in groupedData)
            {
                // Добавить строку группы в таблицу
                var row = table.Rows.Add();
                var cell = row.Cells.Add(group.Key.ToString());
                cell.ColSpan = props.Length;
                cell.BackgroundColor = Pdf.Color.DarkGray;
                cell.DefaultCellTextState.ForegroundColor = Pdf.Color.White;

                foreach (var item in group.Values)
                {
                    // Добавить строку данных в таблицу
                    var dataRow = table.Rows.Add();
                    // Добавить ячейки
                    foreach (var t in props)
                    {
                        var dataItem = t.GetValue(item, null);

                        if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                            switch (dataType.DataType)
                            {
                                case DataType.Currency:
                                    dataRow.Cells.Add(string.Format("{0:C}", dataItem));
                                    break;
                                case DataType.Date:
                                    var dateTime = (DateTime)dataItem;
                                    if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                                    {
                                        dataRow.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                            ? dateTime.ToShortDateString()
                                            : string.Format(df.DataFormatString, dateTime));
                                    }
                                    break;
                                default:
                                    dataRow.Cells.Add(dataItem.ToString());
                                    break;
                            }
                        else
                        {
                            dataRow.Cells.Add(dataItem.ToString());
                        }
                    }
                }
            }
        }
    }

Атрибуты Data Annotations часто используются для описания моделей и помогают нам создать таблицу. Поэтому для ImportEntityList был выбран следующий алгоритм генерации таблицы:

  • строки 12-18: создать строку заголовка и добавить ячейки заголовка согласно правилу “Если присутствует DisplayAttribute, то брать его значение, иначе брать имя свойства”
  • строки 50-53: создать строки данных и добавить ячейки строк согласно правилу “Если определен атрибут DataTypeAttribute, то проверяем, нужно ли делать дополнительные настройки дизайна для него, и в противном случае просто конвертировать данные в строку и добавить в ячейку;”

В этом примере были сделаны дополнительные настройки для DataType.Currency (строки 32-34) и DataType.Date (строки 35-43), но вы можете добавить другие, если это необходимо. Алгоритм метода ImportGroupedData почти такой же, как у предыдущего. Дополнительно используется класс GroupViewModel для хранения группированных данных.

using System.Collections.Generic;
    public class GroupViewModel<K,T>
    {
        public K Key;
        public IEnumerable<T> Values;
    }

Поскольку мы обрабатываем группы, сначала мы генерируем строку для ключевого значения (строки 66-71), а затем - строки этой группы.