Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

147 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Excel.Report.PDF

NuGet Excel.Report.PDF NuGet Excel.Report.PrintDocument License: MIT

A .NET library that converts Excel workbooks into PDF and turns Excel files into reusable, data-driven report templates — without depending on Microsoft Office or COM Interop.

  • Excel → PDF: Pure managed conversion via ClosedXML + PdfSharp.
  • Template engine: Place $symbols and #directives directly in cells and overwrite the workbook with your data at runtime.
  • Multi-page reports: Split long lists across First / Body / Last page templates with automatic page numbering.
  • Built-in renderers: Drop in dynamic images and QR codes from cell directives, or register your own.
  • GDI+ printing: Bind the same rendering pipeline to System.Drawing.Printing.PrintDocument (Windows) for preview / direct printing.
Excel → PDF Quotation template

Table of contents


Install

# Core: Excel → PDF + template engine
PM> Install-Package Excel.Report.PDF

# Optional: Bind to System.Drawing.Printing (Windows only — preview / printer output)
PM> Install-Package Excel.Report.PrintDocument

Excel.Report.PDF targets .NET 6.0 and runs on Windows / Linux / macOS. Excel.Report.PrintDocument is Windows-only because it depends on GDI+ via System.Drawing.Common.


Quick start

1. Set up a font resolver

PdfSharp does not ship with fonts. Implement IFontResolver once at startup and return whichever font bytes you want PdfSharp to embed.

using PdfSharp.Fonts;

public class CustomFontResolver : IFontResolver
{
    public byte[] GetFont(string faceName)
        => faceName.EndsWith("#b") ? Resources.NotoSansJP_ExtraBold
                                   : Resources.NotoSansJP_Regular;

    public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
    {
        var faceName = familyName;
        if (isBold) faceName += "#b";
        return new FontResolverInfo(faceName);
    }
}

GlobalFontSettings.FontResolver = new CustomFontResolver();

A more sophisticated example that loads fonts directly from the Windows Fonts registry lives in Source/TestWinFormsApp/WindowsInstalledFontResolver.cs.

See docs/getting-started.md for the full setup walkthrough.

2. Convert Excel to PDF

using Excel.Report.PDF;

// Whole workbook → multi-page PDF
using var pdf = ExcelConverter.ConvertToPdf("report.xlsx");
File.WriteAllBytes("report.pdf", pdf.ToArray());

// Specific sheet by 1-based position
using var pdfSheet1 = ExcelConverter.ConvertToPdf("report.xlsx", 1);

// Specific sheet by name
using var pdfNamed = ExcelConverter.ConvertToPdf("report.xlsx", "Summary");

// Stream overloads are also available
using var fs = File.OpenRead("report.xlsx");
using var pdfFromStream = ExcelConverter.ConvertToPdf(fs);

The renderer respects Excel's page setup (paper size, margins, scaling, page breaks, centering) and reproduces fonts, fills, borders (including Double), text rotation, vertical text, and embedded pictures.

Print scaling — set a fixed zoom percentage (PageSetup.Scale) or fit the sheet to the page width (#FitColumn / PageSetup.PagesWide). See docs/special-directives.md → Print scaling.

3. Overwrite a template, then convert

Drop $symbols and #directives straight into your .xlsx template, then bind a data object at runtime.

using ClosedXML.Excel;
using Excel.Report.PDF;

var data = new Quotation
{
    Title = "Banquet ingredients",
    Client = "Excel Consulting Inc.",
    PersonInCharge = "Shoichi Otani",
};
data.Details.Add(new() { Title = "Sea bream", Detail = "Fresh",      Price = 10000, Discount = 0    });
data.Details.Add(new() { Title = "Yellowtail", Detail = "Fresh",     Price = 20000, Discount = 0    });
data.Details.Add(new() { Title = "Hamachi",    Detail = "Bargain",   Price = 30000, Discount = 2000 });
data.Details.Add(new() { Title = "Octopus",    Detail = "Bargain",   Price = 40000, Discount = 1000 });

using var book = new XLWorkbook("Quotation.xlsx");

// Overwrite a single sheet
await book.Worksheet(1).OverWrite(new ObjectExcelSymbolConverter(data));

// ...or overwrite every sheet (and expand multi-page #PagedLoopRows templates)
// await book.OverWrite(new ObjectExcelSymbolConverter(data));

// Render the populated workbook to PDF
using var ms = new MemoryStream();
book.SaveAs(ms);
using var pdf = ExcelConverter.ConvertToPdf(ms, 1);
File.WriteAllBytes("Quotation.pdf", pdf.ToArray());

ObjectExcelSymbolConverter resolves symbols against the public properties of the bound object (and supports nested loops). To map symbols to a database row, an API response, or any other source, implement IExcelSymbolConverter — see docs/template-overwrite.md.


Cell directive reference

All directives live in cell text. $ resolves to a value; # invokes a function, loop, or rendering command.

Directive Where Purpose
$name any cell Replace the cell value with converter.GetData("name").
#LoopRow($items, item, n) column A Insert-mode loop. Copies n rows above the current row, once per element of $items.
#LoopRowData($items, item, n) column A Data-only loop. Reuses the existing row format and writes values without inserting rows.
#PagedLoopRows(pageType, rowsPerPage, $items, item, blockRowCount) column A Distributes a long list across First / Body / Last template sheets — see docs/multi-page.md.
#Image($bytesOrStream[, widthScale[, heightScale]]) any cell Insert a picture from byte[] or Stream.
#QR($text[, pixelsPerModule]) any cell Insert a QR code (PNG, ECC level M). Default pixelsPerModule = 10.
#Page any cell except column A Render the current page number when converting to PDF.
#PageCount any cell except column A Render the total page count (resolved after layout).
#PageOf("/") any cell except column A Render current<separator>total. The separator is the literal in the parentheses.
#Empty any cell Reserve the cell area for layout calculations but draw nothing.
#FitColumn A1 only Scale the rendered output so the used column width fills the printable page width (see Print scaling).

Multiple cell directives can coexist on the same cell separated by | (for example #Empty | #FitColumn).

Detailed semantics, edge cases, and worked examples are split across the documents below.


Extending with your own #Function

#Image and #QR are themselves implementations of the public IOverWriteFunction interface. You can register additional #YourName(...) directives in two lines of code — there are no internal hooks involved.

using ClosedXML.Excel;
using Excel.Report.PDF;

public sealed class UpperFunction : IOverWriteFunction
{
    public string Name => "Upper";   // matched as "#Upper(...)"

    public Task InvokeAsync(IXLWorksheet sheet, int rowIndex, int colIndex, object?[] args)
    {
        var text = args.ElementAtOrDefault(0)?.ToString() ?? string.Empty;
        sheet.Cell(rowIndex, colIndex).SetValue(XLCellValue.FromObject(text.ToUpperInvariant()));
        return Task.CompletedTask;
    }
}

// Register once at startup
ExcelOverWriter.RegisterOverWriteFunction(new UpperFunction());

Then in any cell:

#Upper($Client)

args already has $symbols resolved by your IExcelSymbolConverter. See docs/built-in-functions.md for argument-parsing rules, real-world recipes (barcodes, signature stamps, computed totals, async DB lookups), and the full extensibility contract.


Detailed documentation


Requirements

Package Target Key dependencies
Excel.Report.PDF net6.0 ClosedXML 0.105.x, PdfSharp 6.2.x, QRCoder 1.7.x
Excel.Report.PrintDocument net6.0 (Windows) Excel.Report.PDF, System.Drawing.Common 8.0.x

Both libraries are pure managed code — no Microsoft Office, no COM, no native interop.


License

MIT © Codeer Software

About

No description, website, or topics provided.

Resources

Stars

16 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages