Reports Using LayoutEngine and Designer
Beginning with Core Suite Version 12, DynamicPDF redesigned DynamicPDF Designer from the ground up. DynamicPDF Core Suite's LayoutEngine and DynamicPDF Designer Online both make it easy to create reports based on your business objects, JSON, or directly from a database using SQL.
- Using JSON Layout Data
- Using Business Objects
- Using Database Layout Data (JSON)
- Using Database Layout Data (SQL)
- GitHub Project
- Getting Started
- Available on Other Platforms
Here, we demonstrate creating a PDF from a DLEX file and layout data from:
- a JSON document,
- a business object hierarchy,
- SQL that outputs JSON,
- and SQL results directly.
Using JSON Layout Data
Let's first obtain the needed layout data from a JSON document (subreport.json
available from the GitHub project). The following is a snippet from the JSON document.
"ProductsByCategory": [
{
"Name": "Beverages",
"Products": [
{
"ProductID": 1,
"ProductName": "Chai",
"QuantityPerUnit": "10 boxes x 20 bags",
"Discontinued": false,
"UnitPrice": 18
},
...
Note that the Product
element is anonymous. Refer to JSON and JSON Required Formatting for more information on creating LayoutEngine
compliant JSON.
The following example illustrates creating a PDF using JSON layout data.
static void JsonVersion(String dlexString, String jsonString)
{
string outputPdf = BASE_PATH + "subreport_json_output.pdf";
DocumentLayout docLayout = new DocumentLayout(dlexString);
LayoutData layoutData = new LayoutData(
JsonConvert.DeserializeObject(
File.ReadAllText(jsonString)
));
Document document = docLayout.Layout(layoutData);
document.Draw(outputPdf);
}
The example first creates a new DocumentLayout
instance by loading the DLEX document (subreport.dlex
available from the GitHub project). And then deserializing the JSON document into a .NET object hierarchy (Json.NET deserialization documentation). It loads the resulting objects into a LayoutData
* instance, representing the data used to layout the document. It then creates a new Document
instance by combining the DLEX instructions and LayoutData
to produce a PDF.
Using JSON as layout data is the easiest and most flexible way to work with DLEX to create PDF reports.
Using Business Objects
Often you have your own business objects that you wish to use to supply data to a report. Let's illustrate creating a PDF using an user-defined object hierarchy. The code example has two classes, Category
and Product
.
class Category
{
public string Name;
public List<Product> Products;
}
class Product
{
public int ProductID;
public string ProductName;
public string QuantityPerUnit;
public Boolean Discontinued;
public double UnitPrice;
}
The example then creates a List
from the classes and uses it to process the DLEX file.
private static void NameValuesExample(String dlexString)
{
DocumentLayout documentLayout = new DocumentLayout(dlexString);
List<Category> productsByCategory = new List<Category>();
Category a = new Category
{
Name = "Beverages",
Products = new List<Product>() {
new Product {
ProductID = 1, ProductName = "Chai",
QuantityPerUnit = "10 boxes x 20 bags",
Discontinued = false, UnitPrice = 12.22
},
new Product {
ProductID = 2, ProductName = "Chang",
QuantityPerUnit = "5 boxes x 10 bags",
Discontinued = true, UnitPrice = 9.99
}
}
};
Category b = new Category
{
Name = "Condiments",
Products = new List<Product>() {
new Product {
ProductID = 3, ProductName = "Aniseed Syrup",
QuantityPerUnit = "20 boxes x 2 bottles",
Discontinued = true, UnitPrice = 3.92
},
new Product {
ProductID = 4, ProductName = "Chef Anton's Cajun Seasoning",
QuantityPerUnit = "10 boxes x 10 containers",
Discontinued = false, UnitPrice = 3.23
}
}
};
productsByCategory.Add(a);
productsByCategory.Add(b);
LayoutData layoutData = new LayoutData();
layoutData.Add("ProductsByCategory", productsByCategory);
Document document = documentLayout.Layout(layoutData);
document.Draw(Util.GetPath("Output/subrep-object_output.pdf"));
}
The LayoutData
instance loads the list of Category
instances - each of which has a list of Products
- and uses the data and DLEX file to create a PDF.
Using Database Layout Data
Although using a JSON document or a business object hierarchy is an easy way to get layout data, obtaining the data from a database is often more feasible. The following two examples illustrate obtaining data directly from the Northwinds database.
Refer to the blog post: Creating PDF Reports with Data (SQL) for more information on using SQL with DLEX to create PDFs.
The JSON document provides the needed tables and columns from the Northwinds database.
"ProductsByCategory": [
{
"Name": "Beverages",
"Products": [
{
"ProductID": 1,
"ProductName": "Chai",
"QuantityPerUnit": "10 boxes x 20 bags",
"Discontinued": false,
"UnitPrice": 18
},
...
The relevant tables are Categories
and Products
.
Translate the JSON to a SQL SELECT statement to obtain a Product
listing grouped by Category
.
select CategoryName Name, ProductID, ProductName, QuantityPerUnit, Discontinued, UnitPrice
from Products, Categories as ProductsByCategory
where Products.CategoryID = ProductsByCategory.CategoryID
order by CategoryName
Note that we provided the alias Name for CategoryName
and ProductsByCategory
for Categories. The aliases are because the DLEX and original JSON document specified these names.
Using Database Layout Data (JSON)
The first database example we provide creates a JSON document in memory from a SQL query. In the following example, we query the data using SQL and format the output as JSON.
static void DatabaseVersionWithJson(String dlexString)
{
string outputPdf = BASE_PATH + "subreport_db_json_output.pdf";
string sql = "select CategoryName Name, ProductID, ProductName, "
+ "QuantityPerUnit, Discontinued, UnitPrice "
+ "from Products, Categories as ProductsByCategory "
+ "where Products.CategoryID = ProductsByCategory.CategoryID "
+"order by CategoryName for json auto, root('ProductsByCategory')";
var jsonResult = new StringBuilder();
using (var conn = new SqlConnection(CONNECTION_STRING))
{
using (var cmd = new SqlCommand(sql, conn))
{
conn.Open();
var reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
jsonResult.Append("[]");
}
else
{
while (reader.Read())
{
jsonResult.Append(reader.GetValue(0).ToString());
}
}
}
}
DocumentLayout docLayout = new DocumentLayout(dlexString);
LayoutData layoutData = new LayoutData(
JsonConvert.DeserializeObject(
jsonResult.ToString()
));
Document document = docLayout.Layout(layoutData);
document.Draw(outputPdf);
}
The example specifies the query's output as JSON using the for json auto, root('ProductsByCategory')
clause, assigning ProductsByCategory
as the root element and auto
to allow the select statement to determine the JSON output format.
Refer to Format Query Results as JSON for more information.
The example then deserializes the JSON, loads it into a LayoutData
instance, and uses the DocumentLayout
instance and LayoutData
instance to create the PDF document.
If using SQL Server, then the for json
clause is an easy way to get the needed JSON while developing and testing your report. Save the JSON output as a file, upload it to your cloud storage space, and use the JSON to test your report in Designer.
Using Database Layout Data (SQL)
Let's now illustrate creating a PDF using results directly from querying the Northwinds database. Because the DLEX consists of a report and a subreport, we define a ReportDataRequired
event handler to trigger an event when the DLEX requires layout data.
static void DatabaseVersionSql(String dlexString)
{
string outputPdf = BASE_PATH + "subreport_db_sql_output.pdf";
DocumentLayout documentLayout = new DocumentLayout(dlexString);
documentLayout.ReportDataRequired += DocumentLayout_ReportDataRequired;
LayoutData layoutData = new LayoutData();
Document document = documentLayout.Layout(layoutData)
document.Draw(outputPdf);
}
The ReportDataRequired
event handler triggers an event and executes a callback function whenever a report or subreport element is parsed in a DLEX file. The callback function must have the same signature as the ReportDataRequired
event's ReportDataRequiredEventHandler
.
public delegate void ReportDataRequiredEventHandler(object sender, ReportDataRequiredEventArgs args);
The callback passes the sender
and ReportDataRequiredEventArgs
as parameters. The ReportDataRequiredEventArgs
arguments consist of the DataProviderStack
, ReportData
, ElementId
, and DataName
.
A callback's implementation method is where the dynamic processing occurs that replaces the records in the DLEX template with data obtained from an SQL query.
private static void DocumentLayout_ReportDataRequired(object sender, ReportDataRequiredEventArgs args)
{
if (args.ElementId == "ProductsByCategoryReport")
{
string sqlString = "SELECT CategoryID, CategoryName Name FROM Categories ";
SqlConnection connection = new SqlConnection(CONNECTION_STRING);
SqlCommand command = new SqlCommand(sqlString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
args.ReportData = new DataReaderReportData(connection, reader);
}
else if (args.ElementId == "ProductsByCategorySubReport")
{
string sqlString = "SELECT ProductID, ProductName, QuantityPerUnit, "
+ "UnitPrice, Discontinued FROM Products WHERE CategoryID = "
+ args.Data["CategoryID"]
+ " ORDER BY ProductName ";
SqlConnection connection = new SqlConnection(CONNECTION_STRING);
SqlCommand command = new SqlCommand(sqlString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
args.ReportData = new DataReaderReportData(connection, reader);
}
}
When fired, the event handler checks the id of the report and the subreport id and then requests the needed data. The subreport.dlex
DLEX contains a Report
element with the id
ProductsByCategoryReport
and a Subreport
element with the id
ProductsByCategorySubReport
.
The record processing is straightforward. Each record from the SQL dataset gets replaced by a record element in the DLEX file. However, the event handler is fired twice. The first time fired, it processes the main report by querying the data source for category data. The second time fired, it processes the subreport product data. A complete PDF is created by applying the data from the two datasets to the DLEX template.
GitHub Project
Clone or view the example project at GitHub. This example code is contained in the LayoutEngineExample.cs
file.
Getting Started
Obtain the NuGet package to get started using DynamicPDF Core Suite. Login to DynamicPDF API to obtain a free account and start using DynamicPDF Designer Online.
NuGet Package
DynamicPDF Core Suite is available on NuGet and is part of the ceTe.DynamicPDF.CoreSuite.NET
package. The easiest way to install the package is through the Visual Studio Package Manager. You can also download the package directly from NuGet.
DynamicPDF Core Suite Information
More information on DynamicPDF Core Suite can be found on its website.
DynamicPDF API
Use your existing DynamicPDF Core Suite username and password to login to DynamicPDF API and begin using Designer. If you do not have an account then you can a free account and begin using Designer.
Available on Other Platforms
DynamicPDF Core Suite is also available for the Java and COM/ActiveX platforms. Refer to the respective product pages for more details.
- Java - DynamicPDF Generator for Java
- COM/ActiveX - DynamicPDF Generator for COM/ActiveX