Generating PDF and DOCX Reports with Charts and Barcodes in .NET

Introduction

Dynamic reports often contain more than formatted text. In this white paper, we investigate how to programmatically build a report for a fictional investment fund fact sheet. This fact sheet requires the report to contain information displayed as plain text (description, manager comments), tables (total return, portfolio allocation), and of course the corresponding charts for growth, drawdown, etc. Further, the article illustrates how to insert a barcode inside the report header and footer.

NOV Rich Text Editor, in combination with NOV Chart and NOV Barcode, provides all the required functionality to programmatically generate this report.

This article follows the Sample Fund Fact Sheet Report example, distributed with the examples shipped with NOV.

Please note that all data, holdings, company names, etc. are fictional and used solely for the purposes of the example.

What the Sample Report Contains

The sample report is divided into four sections describing the imaginary investment fund. These are:

  • Portfolio overview: contains information about the managers, investment principles, the Morningstar style box, along with some basic portfolio facts.
  • Performance review: contains information about the fund returns - like accumulated returns, annualized returns and compares those returns to a fictional benchmark index.
  • Portfolio positioning: contains information about the fund sector and market-cap exposure, and lists the top sample holdings.
  • Risk and manager commentary: comments about the fund performance from its managers and how it performs relative to its benchmark.

All pages in the report use the same header / footer. This approach is commonly used to enforce company branding.

All code used in the review is not OS-dependent, so the code can work on any OS / platform supported by .NET Core.

This is what the generated report looks like when paginated:

page 01page 02page 03
page 04page 05page 06

Note: Some pages are intentionally left blank to illustrate the next page section break type feature.

Initialize the NOV Components

The first thing to do is to install NOV in the hosting application or web service - the preferred way to do that is to simply install the Nevron Open Vision NuGet package from NuGet.

Afterward, all the functionality used in this sample is contained in the following namespaces:

using Nevron.Nov;
using Nevron.Nov.Barcode;
using Nevron.Nov.Chart;
using Nevron.Nov.Text;
								

Create the Document Sections

The sample report, as discussed above, contains four sections and each of them is placed inside a separate document section. The code that creates a new section is centralized inside the CreateReportSection method and then each section has its own building method (BuildOverview, BuildPerformanceReview, etc.). This is what the core of the report generator looks like:

NSection overview = CreateReportSection("PORTFOLIO OVERVIEW");
m_RichText.Content.Sections.Add(overview);
BuildOverview(overview);

NSection analysis = CreateReportSection("PERFORMANCE REVIEW");
analysis.BreakType = ENSectionBreakType.NextPage;
m_RichText.Content.Sections.Add(analysis);
BuildPerformanceReview(analysis);

NSection positioning = CreateReportSection("PORTFOLIO POSITIONING");
positioning.BreakType = ENSectionBreakType.NextPage;
m_RichText.Content.Sections.Add(positioning);
BuildPortfolioPositioning(positioning);

NSection risk = CreateReportSection("RISK AND MANAGER COMMENTARY");
risk.BreakType = ENSectionBreakType.NextPage;
m_RichText.Content.Sections.Add(risk);
BuildRiskAndCommentary(risk);
								

Isolating the section creation code in a common method ensures that all sections in the document share the same page size, margins, header/footer, etc.

This is essential because NOV Rich Text Editor for .NET supports different page sizes and headers / footers per section and, in this particular case, we want to keep the appearance of the document uniform.

Report Styling

This report example uses the MS Word-like styling implemented in NOV Rich Text Editor for .NET which basically consists of styles applied at character, paragraph and table levels. NOV Rich Text also supports CSS-like styling but this is out of the scope of the current article.

The code defining the styles is centralized in the CreateReportStyles method and defines separate styles for different parts of the report like title, subtitle, section headings, body text, table text, etc.

The code to create an individual style looks like:

m_HeadingStyle = CreateStyle("ReportHeading", 11, ENFontStyle.Bold, NColor.White);
m_HeadingStyle.ParagraphRule = new NParagraphRule();
m_HeadingStyle.ParagraphRule.BackgroundFill = new NColorFill(Navy);
								

This code, for example, applies to paragraphs and modifies the default paragraph font size and style (bold) and assigns dark blue to the paragraph background. This style is used for headings.

Populate Tables from Application Data

The example keeps the report data like fictional holdings, sector weights, etc. in sample structures named Holding, Manager, etc. to abstract the source of data from the data itself. In a real application, those values will come from a SQL database or other data source, so the current code tries to keep the example as close as possible to the code needed to generate a real programmatic report. Then the code that outputs text / chart elements simply has to loop through the data; for example, the holdings table is created as follows:

NTable holdings = CreateDataTable(new double[] { 53, 32, 15 });
AddDataRow(holdings, new string[] { "Fictional company", "Sector", "% of net assets" }, true, false);
for (int i = 0; i < Holdings.Length; i++)
{
	AddDataRow(holdings, new string[]
	{
		Holdings[i].Name, Holdings[i].Sector, FormatPercent(Holdings[i].Weight)
		}, false, false);
	}
AddDataRow(holdings, new string[] { "Total top 10", "", FormatPercent(SumWeights(Holdings)) }, false, true);
								

Embed NOV Chart in the Report

Most reports contain charts for a good reason. Charts are much easier to read and comprehend for managers and other users of the report than plain data as it is easier to spot trends and compare different entities, allocations, etc.

In the context of this example, this is achieved by using the NWidgetInline, which is a special inline type that allows paragraphs in NOV Rich Text Editor to embed any other widget inside NOV. This, of course, includes NOV Chart, NOV Diagram, etc.

The code that creates a paragraph placeholder for such widgets is implemented in the CreateWidgetParagraph method and creates a paragraph placeholder for the widget. It accepts an NWidget, places it in NWidgetInline, and adds that inline to an NParagraph:

NWidgetInline widgetInline = new NWidgetInline();
widgetInline.Content = widget;

NParagraph paragraph = new NParagraph();
paragraph.SpaceBeforeAuto = false;
paragraph.SpaceAfterAuto = false;
paragraph.Margins = new NMargins(0, 2, 0, 4);
paragraph.AvoidPageBreaksInside = true;
paragraph.Inlines.Add(widgetInline);
								

The resulting paragraph can be inserted as a regular paragraph inside the document flow. The same approach is used for the embedded barcode.

The NOV Chart API usage is outside of the scope of this topic, but suffice it to say that it is one of the most feature-rich charting controls for .NET, so it can create almost any type of chart. Most charting types used in reporting are the simple 2D variations of the bar, pie and line, which are illustrated in the report example. However, NOV Chart can also display many specialized, advanced 2D/3D financial, statistical and scientific charts and therefore can be used for more specialized report types as well.

Embed NOV Barcode in the Report

CreateReportFooter uses NOV Barcode to encode the report identifier. The sample uses Code 128 as the barcode encoding, but you can of course use any other barcode encoding as required by the company/organization generating the report. This is achieved with the following code:

NLinearBarcode barcode = new NLinearBarcode();
barcode.Symbology = ENLinearBarcodeSymbology.Code128;
barcode.Text = ReportSerialNumber;
barcode.SizeMode = ENBarcodeSizeMode.Fit;
barcode.PreferredWidth = 360;
barcode.PreferredHeight = 48;
barcode.BackgroundFill = new NColorFill(NColor.White);
// White padding leaves space around the bars for reliable scanning.
barcode.Padding = new NMargins(24, 4, 24, 4);
								

As discussed above, the barcode is embedded in the text document through the NWidgetInline, same as in the case with the chart.

Export the Report

Finally, after the code is executed, the rich text view is populated with the formatted report data. To export it to an actual file you need to use the SaveToLocalFile method of the view, passing the full file name - for example:

m_RichText.SaveToLocalFile(@"C:\Reports\SampleManagedFundReport.pdf");
								

To change the export format to DOCX, for example, all you have to do is to change the file extension:

m_RichText.SaveToLocalFile(@"C:\Reports\SampleManagedFundReport.docx");
								

The choice of format matters depending on the type of distribution / usage of the report. It is better to export in PDF format if those reports are going to stay static, and/or be distributed to users on many different platforms such as macOS, Android, iOS, etc. For reports that will be further edited by a human, it's better to export them as DOCX.

Adapting the Sample

This is a fictional report sample that can easily be adapted for commercial use. To do so, you can follow those steps:

  • Change the fund name, reporting date, share-class label, and report identifier together.
  • Replace the sample static data with data fetched from a database or other data source.
  • Review the final PDF and DOCX output and scan the barcode before adopting the layout for a production report.

Conclusion

NOV Rich Text Editor fully provides the document model and extensibility needed to generate a real fund fact sheet report. Embedding NOV Chart into the portfolio turns raw performance data into charts, while NOV Barcode adds a machine-readable document identifier. The generated report can be output in any of the commonly used report formats - PDF, DOCX, RTF or HTML, and distributed to users. Finally, all this functionality is achieved through completely managed, cross-platform code, which ensures that the service or application generating those reports is both secure and portable.

About Nevron Software

Founded in 1998, Nevron Software is a component vendor specializing in the development of premium presentation layer solutions for .NET-based technologies. Today, Nevron has established itself as a trusted partner worldwide for .NET LOB applications, SharePoint portals, and reporting solutions. Nevron technology is used by many Fortune 500 companies, large financial institutions, global IT consultancies, academic institutions, governments, and non-profits.
For more information, visit: www.nevron.com.
  
For systems integrators like myself, the biggest challenge in my work is the speed in which I can prototype solutions for a customer. This models how Nevron has approached their third-party components, and it is highly appreciated. The first step in my job is to assess a vendor, so I give a technical look to the API; Nevron won because their framework is robust, and other developers recommended it. Secondly, I have to build prototypes based on customer projects, and there was a particularly complex chart I needed to develop using .csv importing, DataSet handling, and stacked bars. The supportive sales staff knew how to escalate my call properly, and I quickly started a dialogue with a charting engineer. I received a prototype which was exactly as I required. Finally, questions about appearance properties were on my list, and Nevron still came through with fast snippets and insights.

Technical support is critical to this process, and Nevron's staff is their company's most valuable asset. I got my business questions answered with enthusiasm. I got a few questions "outside the scope of charting" answered by the engineers, too, so one can be assured that solutions can be acquired even when I have only taken a cursory look at the help files and samples.

The way I do my job is fast and varied. Nevron was able to keep up with my pace, and I will recommend this product and its support subscription to all my friends and colleagues in the business. Keep up the good work.
  

Pete Schieck, Systems Integrator