NuvTools.Report.Pdf 10.2.2

dotnet add package NuvTools.Report.Pdf --version 10.2.2
                    
NuGet\Install-Package NuvTools.Report.Pdf -Version 10.2.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NuvTools.Report.Pdf" Version="10.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NuvTools.Report.Pdf" Version="10.2.2" />
                    
Directory.Packages.props
<PackageReference Include="NuvTools.Report.Pdf" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NuvTools.Report.Pdf --version 10.2.2
                    
#r "nuget: NuvTools.Report.Pdf, 10.2.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NuvTools.Report.Pdf@10.2.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NuvTools.Report.Pdf&version=10.2.2
                    
Install as a Cake Addin
#tool nuget:?package=NuvTools.Report.Pdf&version=10.2.2
                    
Install as a Cake Tool

NuvTools.Report

NuGet License

A .NET library suite for generating reports in PDF, Excel, and CSV formats, plus reading/importing CSV and fixed-length (positional) files. Build structured table-based reports with styling, company branding, and reflection-based data binding. Targets .NET 8, .NET 9, and .NET 10.

Libraries

Library Description NuGet
NuvTools.Report Core report model, PDF abstractions, and Sheet abstractions (CSV, Excel, fixed-length) with attribute-based field mapping and custom converters NuGet
NuvTools.Report.Pdf PDF generation implementation using QuestPDF and PDFsharp NuGet
NuvTools.Report.Sheet Excel, CSV, and fixed-length file implementation using ClosedXML NuGet

Installation

# For PDF export
dotnet add package NuvTools.Report.Pdf

# For Excel/CSV export and CSV/fixed-length reading
dotnet add package NuvTools.Report.Sheet

# Or install the base library only (abstractions and models)
dotnet add package NuvTools.Report

Features

  • Export: PDF (landscape A4), Excel (.xlsx), CSV with configurable delimiters
  • Import: CSV and fixed-length (positional) file reading with attribute-based mapping
  • Data binding: Automatic population from object collections via reflection
  • Custom converters: Extensible field conversion with IFieldConverter / FieldConverter<T>
  • Styling: Customizable colors, fonts, and formatting
  • Branding: Company logos, names, and URLs in headers
  • Metadata: Titles, filter descriptions, issue dates, user info
  • Output: Base64-encoded output for API transmission
  • Multi-table: Documents with separate worksheets/pages
  • PDF merging: Combine multiple PDFs into one
  • DI integration: AddReportServices() and AddPdfReportServices() extensions

Architecture

Namespace Structure

The library follows an abstraction-implementation pattern:

Abstraction (NuvTools.Report) Implementation
NuvTools.Report.Pdf NuvTools.Report.Pdf.* (QuestPDF + PDFsharp)
NuvTools.Report.Sheet.Csv NuvTools.Report.Sheet.Csv (ClosedXML)
NuvTools.Report.Sheet.Excel NuvTools.Report.Sheet.Excel (ClosedXML)
NuvTools.Report.Sheet.FixedLength NuvTools.Report.Sheet.FixedLength
NuvTools.Report.Sheet.Parsing Shared parsing utilities and converters

Document Model

The library uses a document-table-component hierarchy:

Document
└── Tables (List<Table>)
    ├── Info (metadata: name, title, company info, issue date/user)
    ├── Style (formatting: colors, fonts)
    └── Content (Body)
        ├── Header
        │   └── Columns (List<Column>)
        └── Rows (List<Row>)
            └── Cells (List<Cell>)

Usage

Dependency Injection Setup

using NuvTools.Report.Sheet.Extensions;
using NuvTools.Report.Pdf.Extensions;

builder.Services.AddSheetReportServices();    // ICsvReader, ICsvExporter, IFixedLengthReader, IExcelExporter
builder.Services.AddPdfReportServices(); // IPdfExporter, IPdfMerger

Building a Document

using NuvTools.Report.Table.Models;
using NuvTools.Report.Table.Models.Components;

// Define columns
var columns = new List<Column>
{
    new() { Name = "Id", Label = "ID", Order = 1, Format = "" },
    new() { Name = "Name", Label = "Product Name", Order = 2, Format = "" },
    new() { Name = "Price", Label = "Price", Order = 3, Format = "C2" },
    new() { Name = "Date", Label = "Date", Order = 4, Format = "yyyy-MM-dd" }
};

// Create a table
var table = new Table
{
    Info = new Info
    {
        Name = "Products",
        Title = "Product Catalog",
        CompanyAbbreviation = "ACME Corp",
        CompanyUrl = "https://acme.com",
        FilterDescription = "All Products",
        IssueDate = DateTime.Now,
        IssueUser = "john.doe"
    },
    Style = new Style
    {
        BackgroundHeaderColor = "#003366",
        FontHeaderColor = "#FFFFFF"
    },
    Content = new Body()
};

// Populate rows from objects using reflection
table.SetRows(columns, products);

// Create document
var document = new Document
{
    BackgroundDocumentHeaderColor = "#003366",
    Tables = [table]
};

PDF Export

using NuvTools.Report.Pdf;

// Via DI
public class ReportService(IPdfExporter pdfExporter, IPdfMerger pdfMerger)
{
    public string GeneratePdf(Document document)
    {
        // Export first table as base64 PDF
        return pdfExporter.ExportFirstSheetToPdf(document);
    }

    public List<string> GenerateAllPdfs(Document document)
    {
        // Export all tables as separate base64 PDFs
        return pdfExporter.ExportSheetToPdf(document);
    }

    public byte[] MergePdfs(IEnumerable<byte[]> pdfs)
    {
        return pdfMerger.Merge(pdfs);
    }
}

Excel Export

using NuvTools.Report.Sheet.Excel;

// Via DI
public class ReportService(IExcelExporter excelExporter)
{
    public string GenerateExcel(Document document)
    {
        // Returns base64-encoded .xlsx with styled headers, column definitions, and data rows
        string base64 = excelExporter.ExportToExcel(document);

        // Save to file
        byte[] bytes = Convert.FromBase64String(base64);
        File.WriteAllBytes("report.xlsx", bytes);

        return base64;
    }
}

CSV Export

using NuvTools.Report.Sheet.Csv;

// Via DI
public class ReportService(ICsvExporter csvExporter)
{
    public void ExportCsv(Document document)
    {
        // Default delimiter is comma (,) per RFC 4180
        // Header row (column labels) is included by default
        List<string> csvFiles = csvExporter.ExportToCsv(document);
        string csvBase64 = csvExporter.ExportFirstSheetToCsv(document);

        // Use a different delimiter
        string csvSemicolon = csvExporter.ExportFirstSheetToCsv(document, CsvDelimiter.Semicolon);
        string csvTab = csvExporter.ExportFirstSheetToCsv(document, CsvDelimiter.Tab);

        // Use a custom delimiter
        string csvPipe = csvExporter.ExportFirstSheetToCsv(document, CsvDelimiter.Custom, "||");

        // Export without the header row
        string csvNoHeader = csvExporter.ExportFirstSheetToCsv(document, includeHeader: false);

        // By default, delimiter occurrences in values/headers are removed to prevent CSV corruption
        // Disable sanitization if you want to keep raw values
        string csvRaw = csvExporter.ExportFirstSheetToCsv(document, sanitizeDelimiter: false);
    }
}

CSV Reading / Importing

Map CSV content to strongly-typed objects using attributes:

using NuvTools.Report.Sheet.Csv;
using NuvTools.Report.Sheet.Csv.Attributes;
using NuvTools.Report.Sheet.Parsing;

// Define your record model
[CsvRecord(Delimiter = CsvDelimiter.Semicolon)]
public class ProductRecord
{
    [CsvField(0, Caption = "ID")]
    public int Id { get; set; }

    [CsvField(1, Caption = "Name")]
    public string Name { get; set; } = "";

    [CsvField(2, Caption = "Price", Trim = TrimMode.Both)]
    public decimal Price { get; set; }

    [CsvField(3, Caption = "Date", Format = "yyyy-MM-dd")]
    public DateTime Date { get; set; }

    [CsvField(4)]
    public string? OptionalNote { get; set; }
}

// Read CSV via DI
public class ImportService(ICsvReader csvReader)
{
    public List<ProductRecord> ImportFromString(string csvContent)
    {
        return csvReader.ReadString<ProductRecord>(csvContent);
    }

    public List<ProductRecord> ImportFromBase64(string base64)
    {
        return csvReader.ReadBase64<ProductRecord>(base64);
    }

    public List<ProductRecord> ImportFromStream(Stream stream)
    {
        return csvReader.ReadStream<ProductRecord>(stream, new CsvReaderOptions
        {
            SkipHeader = true,
            IgnoreEmptyLines = true,
            HandleQuotedFields = true
        });
    }

    public List<ProductRecord> ImportWithDelimiterOverride(string csvContent)
    {
        // Override the attribute-level delimiter at runtime
        return csvReader.ReadString<ProductRecord>(csvContent, new CsvReaderOptions
        {
            Delimiter = CsvDelimiter.Comma
        });
    }
}
Generating CSV Headers
using NuvTools.Report.Sheet.Csv;

// Get ordered captions from CsvField attributes
IEnumerable<string> captions = typeof(ProductRecord).GetFieldCaptions();
// ["ID", "Name", "Price", "Date", "OptionalNote"]

// Get a formatted header line
string header = typeof(ProductRecord).GetCsvHeader(CsvDelimiter.Semicolon);
// "ID;Name;Price;Date;OptionalNote"

Fixed-Length (Positional) File Reading

Map fixed-width columns to objects using attributes:

using NuvTools.Report.Sheet.FixedLength;
using NuvTools.Report.Sheet.FixedLength.Attributes;
using NuvTools.Report.Sheet.Parsing;

// Define your record model
[FixedLengthRecord(AllowShorterLines = true)]
public class BankRecord
{
    [FixedLengthField(0, 3)]                          // positions 0-2 (3 chars)
    public string BankCode { get; set; } = "";

    [FixedLengthField(1, 5, Trim = TrimMode.Both)]    // positions 3-7 (5 chars)
    public string BranchCode { get; set; } = "";

    [FixedLengthField(2, 15, Trim = TrimMode.Both)]   // positions 8-22 (15 chars)
    public decimal Amount { get; set; }

    [FixedLengthField(3, 8, Format = "yyyyMMdd")]      // positions 23-30 (8 chars)
    public DateTime TransactionDate { get; set; }

    [FixedLengthField(4, 30, Optional = true, Trim = TrimMode.Right)] // positions 31-60 (optional)
    public string? Description { get; set; }
}

// Read via DI
public class BankImportService(IFixedLengthReader reader)
{
    public List<BankRecord> Import(string content)
    {
        return reader.ReadString<BankRecord>(content);
    }

    public List<BankRecord> ImportWithFilter(Stream stream)
    {
        return reader.ReadStream<BankRecord>(stream, new FixedLengthReaderOptions
        {
            IgnoreEmptyLines = true,
            LineFilter = line => line.StartsWith("D") // only detail lines
        });
    }

    public List<BankRecord> ImportFromBase64(string base64)
    {
        return reader.ReadBase64<BankRecord>(base64);
    }
}

Custom Field Converters

Create custom converters for special parsing logic:

using NuvTools.Report.Sheet.Parsing.Converters;

// Implement IFieldConverter directly
public class BrazilianDecimalConverter : IFieldConverter
{
    public object? Convert(string value, string? format)
    {
        // "000000150" with format "2" → 1.50
        var decimalPlaces = int.Parse(format ?? "2");
        var raw = long.Parse(value);
        return raw / (decimal)Math.Pow(10, decimalPlaces);
    }
}

// Or use the generic base class for type safety
public class PercentageConverter : FieldConverter<decimal>
{
    public override decimal Convert(string value, string? format)
    {
        return decimal.Parse(value) / 100m;
    }
}

// Use in attributes
public class FinancialRecord
{
    [CsvField(0)]
    public string Code { get; set; } = "";

    [CsvField(1, Converter = typeof(BrazilianDecimalConverter), Format = "2")]
    public decimal Amount { get; set; }

    [FixedLengthField(0, 10, Converter = typeof(PercentageConverter))]
    public decimal Rate { get; set; }
}

Built-in Type Conversions

The following types are supported out of the box (no custom converter needed):

Type Notes
string Passed through as-is
int, long, short Parsed with InvariantCulture
decimal, double, float Parsed with InvariantCulture
bool Supports true/false and 1/0
DateTime Supports optional format string (e.g. "yyyyMMdd")
DateOnly Supports optional format string
Guid Standard GUID parsing
Enums Case-insensitive Enum.Parse
Nullable versions All above types supported as T? (empty/whitespace → null)

Dependencies

NuvTools.Report.Pdf

  • QuestPDF
  • PDFsharp

NuvTools.Report.Sheet

  • ClosedXML

Building

This solution uses the .slnx (XML-based solution) format.

dotnet build NuvTools.Report.slnx
dotnet build NuvTools.Report.slnx -c Release
dotnet test NuvTools.Report.slnx

Project Structure

nuvtools-report/
├── src/
│   ├── NuvTools.Report/            # Core models and abstractions
│   │   ├── Pdf/                    # IPdfExporter, IPdfMerger
│   │   ├── Sheet/
│   │   │   ├── Csv/               # ICsvReader, ICsvExporter, attributes, options
│   │   │   ├── Excel/             # IExcelExporter
│   │   │   ├── FixedLength/       # IFixedLengthReader, attributes, options
│   │   │   └── Parsing/           # TrimMode, ParseException, converters
│   │   └── Table/Models/          # Document, Table, Info, Style, Body, Row, Cell, Column
│   ├── NuvTools.Report.Pdf/        # PDF implementation (QuestPDF + PDFsharp)
│   └── NuvTools.Report.Sheet/      # Sheet implementation (ClosedXML)
├── tests/
│   ├── NuvTools.Report.Pdf.Tests/
│   └── NuvTools.Report.Sheet.Tests/
├── NuvTools.Report.slnx
└── README.md

License

This project requires license acceptance. See the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.2.2 29 3/16/2026
10.2.1 79 3/9/2026
10.2.0 80 3/9/2026
10.1.0 231 1/28/2026
10.0.0 252 12/6/2025
9.1.2 600 9/30/2025
9.1.1 433 5/26/2025
9.1.0 197 5/23/2025
9.0.0 184 11/13/2024
8.0.3 216 3/5/2024
7.1.0 268 8/27/2023
7.0.0 312 3/10/2023