Beruflich Dokumente
Kultur Dokumente
Introducing PdfRport
Vahid_N, 14 Feb 2013
PdfReport is a code-first reporting engine which is built on top of the iTextSharp and EPPlus libraries.
Introduction
PdfReport is a code-first reporting engine, which is built on top of the iTextSharp and EPPlus libraries. It's compatible with both .NET
3.5+ Web and Windows applications. PdfReport supports a wide range of data sources from data tables to in-memory strongly
typed lists without needing a database. It saves you time from searching and learning a lot of tips and tricks of iTextSharp and
EPPlus libraries. It's designed to be compatible with RTL languages.
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 1/12
27/03/2019 Introducing PdfRport - CodeProject
a. Create a new Class Library project in Visual Studio. We will use it as the base report classes container for both Windows and
Web applications.
b. Then add new references to the following assemblies: PdfReport, iTextSharp, and EPPlus. You can download them from
http://pdfreport.codeplex.com/releases/
Or just use the NuGet PowerShell console to add these references automatically:
http://nuget.org/packages/PdfReport/
using System.Web;
using System.Windows.Forms;
namespace PdfReportSamples
{
public static class AppPath
{
public static string ApplicationPath
{
get
{
if (isInWeb)
return HttpRuntime.AppDomainAppPath;
return Application.StartupPath;
}
}
We will use this class to specify the location of the produced PDF file. It needs the following references as well:
System.Windows.Forms.dll
System.Web.dll
using System;
namespace PdfReportSamples.IList
{
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string LastName { set; get; }
public long Balance { set; get; }
public DateTime RegisterDate { set; get; }
}
}
//"User" class will be used for creating an in-memory generic list data source.
//And now add the main report class:
using System;
using System.Collections.Generic;
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 2/12
27/03/2019 Introducing PdfRport - CodeProject
using PdfReportSamples.Models;
using PdfRpt.Core.Contracts;
using PdfRpt.FluentInterface;
namespace PdfReportSamples.IList
{
public class IListPdfReport
{
public IPdfReportData CreatePdfReport()
{
return new PdfReport().DocumentPreferences(doc =>
{
doc.RunDirection(PdfRunDirection.LeftToRight);
doc.Orientation(PageOrientation.Portrait);
doc.PageSize(PdfPageSize.A4);
doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid",
Application = "PdfRpt", Keywords = "IList Rpt.",
Subject = "Test Rpt", Title = "Test" });
})
.DefaultFonts(fonts =>
{
fonts.Path(Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\arial.ttf",
Environment.GetEnvironmentVariable("SystemRoot") +
"\\fonts\\verdana.ttf");
})
.PagesFooter(footer =>
{
footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy"));
})
.PagesHeader(header =>
{
header.DefaultHeader(defaultHeader =>
{
defaultHeader.RunDirection(PdfRunDirection.LeftToRight);
defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
defaultHeader.Message("Our new rpt.");
});
})
.MainTableTemplate(template =>
{
template.BasicTemplate(BasicTemplate.ClassicTemplate);
})
.MainTablePreferences(table =>
{
table.ColumnsWidthsType(TableColumnWidthType.Relative);
table.NumberOfDataRowsPerPage(5);
})
.MainTableDataSource(dataSource =>
{
var listOfRows = new List<User>();
for (int i = 0; i < 200; i++)
{
listOfRows.Add(new User { Id = i, LastName = "LastName " + i,
Name = "Name " + i, Balance = i + 1000 });
}
dataSource.StronglyTypedList(listOfRows);
})
.MainTableSummarySettings(summarySettings =>
{
summarySettings.OverallSummarySettings("Summary");
summarySettings.PreviousPageSummarySettings("Previous Page Summary");
summarySettings.PageSummarySettings("Page Summary");
})
.MainTableColumns(columns =>
{
columns.AddColumn(column =>
{
column.PropertyName("rowNo");
column.IsRowNumber(true);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(0);
column.Width(1);
column.HeaderCell("#");
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 3/12
27/03/2019 Introducing PdfRport - CodeProject
});
columns.AddColumn(column =>
{
column.PropertyName<User>(x => x.Id);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(1);
column.Width(2);
column.HeaderCell("Id");
});
columns.AddColumn(column =>
{
column.PropertyName<User>(x => x.Name);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(2);
column.Width(3);
column.HeaderCell("Name");
});
columns.AddColumn(column =>
{
column.PropertyName<User>(x => x.LastName);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(3);
column.Width(3);
column.HeaderCell("Last Name");
});
columns.AddColumn(column =>
{
column.PropertyName<User>(x => x.Balance);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(4);
column.Width(2);
column.HeaderCell("Balance");
column.ColumnItemsTemplate(template =>
{
template.TextBlock();
template.DisplayFormatFormula(obj => obj == null ? string.Empty :
string.Format("{0:n0}", obj));
});
column.AggregateFunction(aggregateFunction =>
{
aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty :
string.Format("{0:n0}", obj));
});
});
})
.MainTableEvents(events =>
{
events.DataSourceIsEmpty(message: "There is no data available to display.");
})
.Export(export =>
{
export.ToExcel();
export.ToCsv();
export.ToXml();
})
.Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptIListSample.pdf"));
}
}
}
To use this class and create a new PDF report file, we can write:
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 4/12
27/03/2019 Introducing PdfRport - CodeProject
In the DocumentPreferences method we can specify the direction of the report (PdfRunDirection: RTL or LTR),
page size (PdfPageSize), its orientation (PageOrientation), and so on.
Then it's necessary to determine the default report font files in the DefaultFonts method. The first font will be the main
font and the second font will be used as the fallback font.
PdfReport comes with built-in footer and header samples. It's possible to customize these elements by implementing the
IPageFooter and IPageHeader interfaces. We will discuss it in the other How-To's.
By using MainTableTemplate, we can define the main grid's template. There are some predefined templates available
in the PdfReport library. Also it's possible to create new templates by implementing the ITableTemplate interface.
The MainTablePreferences method will be used for specifying the settings of the main report's grid, such as how
many rows per page should be available (if we don't specify it, rows count will be calculated automatically based on the
page size).
a. Relative: Each column has a relative width equal to 1. Example: Relative values = 2, 1, 1. This means that you
want to divide the width of the table into four parts (2 + 1 + 1): two parts for the first column, one part for columns
two and three.
b. Absolute: The absolute width expressed in user space units.
c. EquallySized: Equally sized columns. In this case, all of the specified widths will be ignored.
d. FitToContent: Tries to resize the columns automatically. In this case, all of the specified widths will be ignored.
The MainTableDataSource method sets the data source of the main grid. For instance, in the above example, the
StronglyTypedList method will process the list of users. There are other built-in data source methods in the
PdfReport library. For example, if you want to use raw SQL and work with the database directly, try the following methods:
It's possible to write parametric queries in all of the above methods. These parameters should start with the @ symbol. Here is a
quick sample which shows how to work with SQLite databases in PdfReport:
dataSource.GenericDataReader(
providerName: "System.Data.SQLite",
connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate]
FROM [tblBlogs]
WHERE [NumberOfPosts]>=@p1",
parametersValues: new object[] { 10 }
);
Add a reference to the System.Data.SQLite assembly and then use the above generic data reader. The same rule applies to MySQL
and other databases.
The MainTableSummarySettings method determines the auto generated summary/total labels and their position to
show. It's optional.
By using the optional MainTableColumns method, it's possible to determine the exact columns of the report's grid.
Each column should be present in the data source. Also it's possible to define the calculated fields as well. It will be
discussed in other How-To's later. In the MainTableColumns method, you can specify the related property of the
column, its width, visibility, order, and so on. Here by using the ColumnItemsTemplate method, we can determine the
type of the current field and how it should be displayed. If it should be displayed as a text, use
the template.TextBlock() method (it's the default method). Also there are some other built-in cell templates such
as image, hyperlinks, etc. It's possible to use custom column templates by implementing the IColumnItemsTemplate
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 5/12
27/03/2019 Introducing PdfRport - CodeProject
interface too.
If you want to format the cell's value before rendering, use the template.DisplayFormatFormula method. It's a
callback method, which gives you the actual value of the cell and then you can format it and return the final result to show
on the report.
By using the column.AggregateFunction method, we can determine the related aggregate method of the current
column. There are some predefined numeric aggregate functions available in the PdfReport library. Also it's possible to write
custom ones by implementing the IAggregateFunc interface.
The MainTableEvents method provides access to the internal events of the main grid. For instance if the data source is
empty, the DataSourceIsEmpty event will be raised.
Also it's possible to export the main table's data as Excel, CSV, XML, etc. files. All of these exported files will be embedded in
the final PDF file automatically.
Here are some tips about customizing the auto generated columns:
If you are using the SQL based data sources such as GenericDataReader, to customize the header cells just define the column
aliases in your final SQL:
By using the MainTableAdHocColumnsConventions method, it's possible to alter the rendering conditions of the dynamic
columns. In the MainTableAdHocColumnsConventions method, we can include the auto generated row column in the
final report:
adHocColumns.ShowRowNumberColumn(true);
adHocColumns.RowNumberColumnCaption("#");
Or it's possible to format a cell's value based on its type:
adHocColumns.AddTypeDisplayFormatFormula(
typeof(DateTime),
data => { return PersianDate.ToPersianDateTime((DateTime)data); }
);
Here we are altering the rendering value of all of the DateTime columns.
adHocColumns.AddTypeAggregateFunction(
typeof(Int64),
new AggregateProvider(AggregateFunction.Sum)
{
DisplayFormatFormula = obj => obj == null ? string.Empty : string.Format("
{0:n0}", obj)
});
If you are using a generic list data source, it's possible to omit the MainTableColumns method and all of its definitions by
replacing them with data annotations:
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 6/12
27/03/2019 Introducing PdfRport - CodeProject
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using PdfReportSamples.Models;
using PdfRpt.Aggregates.Numbers;
using PdfRpt.ColumnsItemsTemplates;
using PdfRpt.Core.Contracts;
using PdfRpt.Core.Helper;
using PdfRpt.DataAnnotations;
namespace PdfReportSamples.DataAnnotations
{
public class Person
{
[IsVisible(false)]
public int Id { get; set; }
[DisplayName("User name")]
//Note: If you don't specify the ColumnItemsTemplate, a new TextBlockField() will be used
automatically.
[ColumnItemsTemplate(typeof(TextBlockField))]
public string Name { get; set; }
[DisplayName("Job title")]
public JobTitle JobTitle { set; get; }
[DisplayName("Date of birth")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime DateOfBirth { get; set; }
[DisplayName("Date of death")]
[DisplayFormat(NullDisplayText = "-", DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime? DateOfDeath { get; set; }
[DisplayFormat(DataFormatString = "{0:n0}")]
[CustomAggregateFunction(typeof(Sum))]
public int Salary { get; set; }
[IsCalculatedField(true)]
[DisplayName("Calculated Field")]
[DisplayFormat(DataFormatString = "{0:n0}")]
[AggregateFunction(AggregateFunction.Sum)]
public string CalculatedField { get; set; }
[CalculatedFieldFormula("CalculatedField")]
public static Func<IList<CellData>, object> CalculatedFieldFormula =
list =>
{
if (list == null) return string.Empty;
var salary = (int)list.GetValueOf<Person>(x =>
x.Salary);
return salary * 0.8;
};//Note: It's a static field, not a property.
}
}
If you don't want to show a property in the final report, use the [IsVisible(false)] attribute.
DisplayName attribute will be used to define the header cells of the report.
Specifying the ColumnItemsTemplate attribute is optional and if it's not defined, TextBlockField will be used
automatically.
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 7/12
27/03/2019 Introducing PdfRport - CodeProject
AdHocColumns
DataAnnotations
Just set column.IsRowNumber(true);. And then a new row number column which is not included in the data source will be
available in the final report.
columns.AddColumn(column =>
{
column.PropertyName("CF1");
column.CalculatedField(
list =>
{
if (list == null) return string.Empty;
var name = list.GetSafeStringValueOf<User>(x => x.Name);
var lastName = list.GetSafeStringValueOf<User>(x => x.LastName);
return name + " - " + lastName;
});
column.HeaderCell("Full name");
column.Width(3);
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(4);
});
The column.CalculatedField method's argument gives us the list of values of the current row:
Now we have time to build the current cell's value based on the other column values. There are some helper methods defined in
the PdfRpt.Core.Helper namespace such as GetSafeStringValueOf to help working with IList<CellData>
data more easily. Defining a PropertyName is mandatory, but in this case it can be an arbitrary text.
Here is the full sample which shows how to define a calculated column: CalculatedFields
To show these kind of images we need to change the default ColumnItemsTemplate which is TextBlock to
ImageFilePath:
columns.AddColumn(column =>
{
column.PropertyName<ImageRecord>(x => x.ImagePath);
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 8/12
27/03/2019 Introducing PdfRport - CodeProject
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(2);
column.Width(3);
column.HeaderCell("Image");
column.ColumnItemsTemplate(t => t.ImageFilePath(defaultImageFilePath: string.Empty, fitImages:
false));
});
Here defaultImageFilePath is the default image path in the case of missing images.
Showing images stored as binary data in databases is similar to (a). We just need to use the suitable ColumnItemsTemplate
which is the ByteArrayImage template:
columns.AddColumn(column =>
{
column.PropertyName("thumbnail");
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(5);
column.HeaderCell("Image");
column.ColumnItemsTemplate(t => t.ByteArrayImage(defaultImageFilePath: string.Empty, fitImages:
false));
});
Here you can find the complete samples of (a) and (b):
ImageFilePath
DbImagePdfReport
columns.AddColumn(column =>
{
column.PropertyName("Month");
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(2);
column.Width(2);
column.HeaderCell("Month");
column.ColumnItemsTemplate(template =>
{
template.TextBlock();
template.ConditionalFormatFormula(list =>
{
var cellValue = int.Parse(list.GetSafeStringValueOf("Month", nullValue: "0"));
if (cellValue == 7)
{
return new CellBasicProperties
{
PdfFontStyle = DocumentFontStyle.Bold | DocumentFontStyle.Underline,
FontColor = new BaseColor(System.Drawing.Color.Brown),
BackgroundColor = new BaseColor(System.Drawing.Color.Yellow)
};
}
return new CellBasicProperties { PdfFontStyle = DocumentFontStyle.Normal };
});
});
});
template.ConditionalFormatFormula is a callback method which gives us the list of the current row's data. Now based
on the value of the month (or other properties) we can return the "new CellBasicProperties" with a different font color, style, etc. You
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 9/12
27/03/2019 Introducing PdfRport - CodeProject
using System.Collections.Generic;
using System.Drawing;
using iTextSharp.text;
using PdfRpt.Core.Contracts;
namespace PdfReportSamples.HexDump
{
public class GrayTemplate : ITableTemplate
{
public HorizontalAlignment HeaderHorizontalAlignment
{
get { return HorizontalAlignment.Center; }
}
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 10/12
27/03/2019 Introducing PdfRport - CodeProject
{
get { return new BaseColor(ColorTranslator.FromHtml("#333333")); }
}
.MainTableTemplate(template =>
{
template.CustomTemplate(new GrayTemplate());
})
Colors in the iTextSharp library are defined by the BaseColor class. If you want to convert the colors of
System.Drawing to BaseColor, just pass it to the BaseColor's constructor:
There are some useful helper methods in .NET classes such as ColorTranslator.FromHtml to convert and use the
HTML colors.
If you want to define a transparent color here, just return null.
In the ITableTemplate interface, some colors are defined as a list. These properties can accept one or max two colors. If
two colors are specified, an automatic gradient of these colors will be shown on the report.
columns.AddColumn(column =>
{
column.PropertyName("User");
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
column.Order(1);
column.Width(3);
column.HeaderCell("User");
column.CalculatedField(list =>
{
var user = list.GetSafeStringValueOf("User");
var photo = new Uri(list.GetSafeStringValueOf("Photo"));
var image = string.Format("<img src='{0}' />", photo);
return
@"<table style='width: 100%; font-size:9pt;'>
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 11/12
27/03/2019 Introducing PdfRport - CodeProject
<tr>
<td>" + user + @"</td>
</tr>
<tr>
<td>" + image + @"</td>
</tr>
</table>
";
});
column.ColumnItemsTemplate(template =>
{
template.Html(); // Using iTextSharp's limited HTML to PDF capabilities (HTMLWorker class).
});
});
Here iTextSharp's HTMLWorker class is used behind the scenes. By using CalculatedField, we can inject our new value of a
cell and then process it by the selected ColumnItemsTemplate. Note: HTML to PDF capabilities of iTextSharp's
HTMLWorker class are very limited, and don't expect too much about it. You can find this sample here.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2013 by Vahid_N
Web01 | 2.8.190306.1 | Last Updated 14 Feb 2013 Everything else Copyright © CodeProject, 1999-2019
https://www.codeproject.com/Articles/492055/Introducing-PdfRport?display=Print 12/12