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.
The sample report is divided into four sections describing the imaginary investment fund. These are:
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:
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
Note: Some pages are intentionally left blank to illustrate the next page section break type feature.
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;
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.
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.
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);
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.
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.
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.
This is a fictional report sample that can easily be adapted for commercial use. To do so, you can follow those steps: