Интеграция таблицы с источниками данных PDF
Следующий фрагмент кода также работает с библиотекой Aspose.PDF.Drawing.
Интеграция таблицы с базой данных
Базы данных созданы для хранения и управления данными. Для программистов обычной практикой является заполнение различных объектов данными из баз данных. В этой статье обсуждается добавление данных из базы данных в таблицу. Можно заполнить объект Table данными из любого источника данных с помощью Aspose.PDF for .NET. И это не только возможно, но и очень просто.
Aspose.PDF for .NET позволяет разработчикам импортировать данные из:
- Массива объектов
- DataTable
- DataView
В этом разделе представлена информация о получении данных из DataTable или DataView.
Все разработчики, работающие на платформе .NET, должны быть знакомы с основными концепциями ADO.NET, представленными .NET Framework. С помощью ADO.NET можно подключаться практически ко всем типам источников данных. Мы можем получать данные из баз данных и сохранять их в DataSet, DataTable или DataView. Aspose.PDF for .NET предоставляет поддержку импорта данных и из них. Это даёт разработчикам больше свободы для заполнения таблиц в документах PDF из любого источника данных.
Для импорта данных из баз используются методы ImportDataTable(..) и ImportDataView(..) класса Table.
Приведённый ниже пример демонстрирует использование метода ImportDataTable. В этом примере объект DataTable создаётся с нуля, и записи добавляются программно, а не заполняются данными из базы данных. Разработчики также могут заполнять DataTable из базы данных по своему желанию.
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void ImportFromDataTable()
{
// The path to the documents directory
var 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));
// Add 2 rows into the DataTable object programmatically
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);
// Create PDF document
using (var document = new Aspose.Pdf.Document())
{
// Add page
var page = document.Pages.Add();
// Initializes a new instance of the Table
Aspose.Pdf.Table table = new Aspose.Pdf.Table();
// Set column widths of the table
table.ColumnWidths = "40 100 100 100";
// Set the table border color as LightGray
table.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, .5f, Aspose.Pdf.Color.FromRgb(System.Drawing.Color.LightGray));
// Set the border for table cells
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);
// Add table object to first page of input document
page.Paragraphs.Add(table);
// Save PDF document
document.Save(dataDir + "ImportFromDataTable_out.pdf");
}
}
Как определить, разобьётся ли таблица на текущей странице
Таблицы по умолчанию добавляются сверху слева, и если таблица достигает конца страницы, она автоматически разбивается. Вы можете программно получить информацию о том, уместится ли таблица на текущей странице или она разорвётся внизу страницы. По этой причине сначала вам нужно получить информацию о размере документа, затем вам нужно получить поля верхнего и нижнего колонтитула страницы, верхнее поле таблицы и высоту таблицы. Если вы добавите верхнее поле страницы + нижнее поле страницы + верхнее поле таблицы + высоту таблицы и вычтете это из высоты документа, вы получите оставшееся пространство над документом. В зависимости от конкретной высоты строки (которую вы указали), вы можете подсчитать, поместятся ли все строки таблицы в оставшемся пространстве над страницей или нет. Пожалуйста, взгляните на следующий фрагмент кода. В следующем коде средняя высота строки составляет 23,002 пункта.
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void DetermineTableBreak()
{
// The path to the documents directory
var dataDir = RunExamples.GetDataDir_AsposePdf_Tables();
// Create PDF document
using (var document = new Aspose.Pdf.Document())
{
// Add page
var page = pdf.Pages.Add();
// Instantiate a table object
Aspose.Pdf.Table table1 = new Aspose.Pdf.Table();
table1.Margin.Top = 300;
// Add the table in paragraphs collection of the desired section
page.Paragraphs.Add(table1);
// Set with column widths of the table
table1.ColumnWidths = "100 100 100";
// Set default cell border using BorderInfo object
table1.DefaultCellBorder = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, 0.1F);
// Set table border using another customized BorderInfo object
table1.Border = new Aspose.Pdf.BorderInfo(Aspose.Pdf.BorderSide.All, 1F);
// Create MarginInfo object and set its left, bottom, right and top margins
Aspose.Pdf.MarginInfo margin = new Aspose.Pdf.MarginInfo();
margin.Top = 5f;
margin.Left = 5f;
margin.Right = 5f;
margin.Bottom = 5f;
// Set the default cell padding to the MarginInfo object
table1.DefaultCellPadding = margin;
// If you increase the counter to 17, table will break
// Because it cannot be accommodated any more over this page
for (int RowCounter = 0; RowCounter <= 16; RowCounter++)
{
// Create rows in the table and then cells in the rows
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");
}
// Get the Page Height information
float PageHeight = (float)pdf.PageInfo.Height;
// Get the total height information of Page Top & Bottom margin,
// Table Top margin and table height.
float TotalObjectsHeight = (float)page.PageInfo.Margin.Top + (float)page.PageInfo.Margin.Bottom + (float)table1.Margin.Top + (float)table1.GetHeight();
// Display Page Height, Table Height, table Top margin and Page Top
// And Bottom margin information
Console.WriteLine("PDF document Height = " + pdf.PageInfo.Height.ToString() + "\nTop Margin Info = " + page.PageInfo.Margin.Top.ToString() + "\nBottom Margin Info = " + page.PageInfo.Margin.Bottom.ToString() + "\n\nTable-Top Margin Info = " + table1.Margin.Top.ToString() + "\nAverage Row Height = " + table1.Rows[0].MinRowHeight.ToString() + " \nTable height " + table1.GetHeight().ToString() + "\n ----------------------------------------" + "\nTotal Page Height =" + PageHeight.ToString() + "\nCummulative height including Table =" + TotalObjectsHeight.ToString());
// Check if we deduct the sume of Page top margin + Page Bottom margin
// + Table Top margin and table height from Page height and its less
// Than 10 (an average row can be greater than 10)
if ((PageHeight - TotalObjectsHeight) <= 10)
{
// If the value is less than 10, then display the message.
// Which shows that another row can not be placed and if we add new
// Row, table will break. It depends upon the row height value.
Console.WriteLine("Page Height - Objects Height < 10, so table will break");
}
// Save PDF document
document.Save(dataDir + "DetermineTableBreak_out.pdf");
}
}
Добавление повторяющегося столбца в таблице
В классе Aspose.Pdf.Table вы можете установить RepeatingRowsCount, который будет повторять строки, если таблица слишком длинная по вертикали и переносится на следующую страницу. Однако в некоторых случаях таблицы слишком широки, чтобы поместиться на одной странице, и их необходимо продолжить на следующей странице. Для этого мы внедрили свойство RepeatingColumnsCount в класс Aspose.Pdf.Table. Установка этого свойства приведёт к тому, что таблица будет перенесена на следующую страницу по столбцам и повторит заданное количество столбцов в начале следующей страницы. Следующий фрагмент кода показывает использование свойства RepeatingColumnsCount:
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddRepeatingColumn()
{
// The path to the documents directory
var dataDir = RunExamples.GetDataDir_AsposePdf_Tables();
// Create PDF document
using (var document = new Aspose.Pdf.Document())
{
// Add page
var page = document.Pages.Add();
// Instantiate an outer table that takes up the entire page
Aspose.Pdf.Table outerTable = new Aspose.Pdf.Table();
outerTable.ColumnWidths = "100%";
outerTable.HorizontalAlignment = HorizontalAlignment.Left;
// Instantiate a table object that will be nested inside outerTable that will break inside the same page
Aspose.Pdf.Table mytable = new Aspose.Pdf.Table();
mytable.Broken = TableBroken.VerticalInSamePage;
mytable.ColumnAdjustment = ColumnAdjustment.AutoFitToContent;
// Add the outerTable to the page paragraphs
// Add mytable to 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);
// Add header Row
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++)
{
// Create rows in the table and then cells in the rows
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");
}
// Save PDF document
document.Save(dataDir + "AddRepeatingColumn_out.pdf");
}
}
Интеграция таблицы с источником Entity Framework
Более актуальным для современного .NET является импорт данных из ORM-фреймворков. В этом случае рекомендуется расширить класс Table методами расширения для импорта данных из простого списка или сгруппированных данных. Давайте приведём пример для одного из самых популярных ORM — Entity Framework.
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
public static class PdfHelper
{
private 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)
{
// Add row to table
var row = table.Rows.Add();
// Add table cells
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());
}
}
}
}
private 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)
{
// Add group row to table
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)
{
// Add data row to table
var dataRow = table.Rows.Add();
// Add cells
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), а после неё — строки этой группы.