Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ permissions:
contents: write
pull-requests: write
security-events: write
pages: write
id-token: write

jobs:
build:
Expand Down Expand Up @@ -289,3 +291,49 @@ jobs:
--notes "Pre-release from \`${{ github.head_ref }}\` branch." \
--prerelease \
./artifacts/*.nupkg

docs:
needs: release
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && github.event_name == 'push'

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Install DocFX
run: dotnet tool install -g docfx

- name: Restore
run: dotnet restore

- name: Build site
run: docfx docs/docfx.json

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/_site

docs-deploy:
needs: docs
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
concurrency:
group: pages
cancel-in-progress: false
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ obj
/_NCrunch*/*.cache
/*.ncrunchsolution.user
/.claude/settings.local.json

# DocFX generated output
/docs/_site/
/docs/api/
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[![NuGet](https://img.shields.io/nuget/v/Tharga.Reporter)](https://www.nuget.org/packages/Tharga.Reporter)
![Nuget](https://img.shields.io/nuget/dt/Tharga.Reporter)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Docs:** [reporter.tharga.net](https://reporter.tharga.net) — guides and API reference.

A .NET library for building and rendering PDF documents from declarative templates. Define a `Template` of `Section`s with text, images, barcodes, lines, rectangles, and tables, feed it data, and render to a PDF byte array.

Supports .NET 8, .NET 9, and .NET 10. Windows-only at runtime.
2 changes: 1 addition & 1 deletion Tharga.Reporter/Tharga.Reporter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Company>Thargelion AB</Company>
<Product>Tharga Reporter</Product>
<Description>Tharga Reporter generates PDF and prints pdf-documents and send then m diretly to the printer.</Description>
<PackageIconUrl>http://thargelion.se/wp-content/uploads/2019/11/Thargelion-White-Icon-150.png</PackageIconUrl>
<PackageIconUrl>https://thargelion.net/assets/component-reporter.png</PackageIconUrl>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageProjectUrl>https://github.com/Tharga/Reporter</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand Down
1 change: 1 addition & 0 deletions docs/CNAME
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
reporter.tharga.net
176 changes: 176 additions & 0 deletions docs/articles/data-and-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Data and rendering

A template is just the layout. The actual values — invoice number, customer name, line items — come from a `DocumentData` instance passed to the `Renderer`. This page covers the data model, the `Renderer`, and the available page formats.

## DocumentData

Build a `DocumentData` and add key/value pairs:

```csharp
var data = new DocumentData();
data.Add("InvoiceNumber", "INV-00042");
data.Add("CustomerName", "Bob Lablow");
```

Any text element whose `Value` contains `{Key}` tokens will have those tokens replaced at render time:

```csharp
section.Pane.ElementList.Add(new Text { Value = "Invoice {InvoiceNumber}" });
// renders as: "Invoice INV-00042"
```

Pass the data to the `Renderer`:

```csharp
var pdf = new Renderer(template, data).GetPdfBinary(PageSize.A4);
```

If a placeholder references an unknown key, it renders empty. Use `Text.HideValue` to suppress an entire element when a particular key is empty.

## Built-in placeholders

The renderer fills these automatically — you don't need to add them to `DocumentData`:

| Token | Resolves to |
|---|---|
| `{Page}` | Current page number |
| `{TotalPages}` | Total pages in the document |
| `{Section}` | Current section name |

So `"Page {Page} of {TotalPages}"` produces `"Page 1 of 3"`.

## Table data

For `Table` elements, add a `DocumentDataTable` with the same name as the table's `DataTableName`:

```csharp
var lines = new DocumentDataTable("InvoiceLines");
lines.Rows.Add(new DocumentDataTableData(new Dictionary<string, string>
{
{ "Item", "Widget" }, { "Qty", "2" }, { "Price", "10.00" }
}));
lines.Rows.Add(new DocumentDataTableData(new Dictionary<string, string>
{
{ "Item", "Gadget" }, { "Qty", "1" }, { "Price", "25.00" }
}));

var data = new DocumentData();
data.Add(lines);
```

Each `DocumentDataTableData` becomes a row; the dictionary keys must match the `ColumnName` on each `TableColumn` you defined.

For visual grouping, use `DocumentDataTableGroup` between data rows — it renders as a styled separator with its `Content` as the label.

## Renderer

The `Renderer` takes a `Template` plus the optional knobs:

```csharp
var renderer = new Renderer(
template,
documentData: data,
includeBackgroundObjects: true,
documentProperties: properties,
debug: false
);

byte[] pdf = renderer.GetPdfBinary(PageSize.A4);
```

- **`documentData`** — placeholder + table data. `null` for templates with no placeholders.
- **`includeBackgroundObjects`** — set `false` to omit elements marked `IsBackground = true`. Useful when the foreground is rendered onto pre-printed paper.
- **`documentProperties`** — fills the PDF metadata: `Author`, `Title`, `Subject`, `Creator`.
- **`debug`** — draws guides for element bounds, reference points, and the page grid. Off for production.

## DocumentProperties

PDF metadata seen by the reader:

```csharp
var props = new DocumentProperties
{
Title = "Invoice INV-00042",
Author = "Acme Corp",
Subject = "Customer invoice",
Creator = "MyApp 1.2.0"
};
```

## PageFormat

`PageFormat` accepts the PdfSharp `PageSize` enum directly (implicit conversion):

```csharp
renderer.GetPdfBinary(PageSize.A4);
renderer.GetPdfBinary(PageSize.Letter);
renderer.GetPdfBinary(PageSize.A5);
```

For a custom size, pass millimetres:

```csharp
renderer.GetPdfBinary(new PageFormat(customWidth: 100, customHeight: 150));
```

There's also a built-in `PlasticCard` (85x54mm):

```csharp
renderer.GetPdfBinary(PageFormat.PlasticCard);
```

## End-to-end example

A two-page invoice with a header, body table, footer page numbers, and metadata:

```csharp
using PdfSharp;
using Tharga.Reporter;
using Tharga.Reporter.Entity;
using Tharga.Reporter.Entity.Element;

var section = new Section
{
Margin = new UnitRectangle { Left = "1cm", Top = "2cm", Right = "1cm", Bottom = "2cm" }
};

section.Header.Height = "2cm";
section.Header.ElementList.Add(new Text { Value = "Invoice {InvoiceNumber}", FontSize = "18pt" });

var table = new Table
{
Width = "100%",
DataTableName = "Lines",
Columns =
{
new TableColumn { Title = "Item", ColumnName = "Item", Width = "60%" },
new TableColumn { Title = "Qty", ColumnName = "Qty", Width = "20%" },
new TableColumn { Title = "Price", ColumnName = "Price", Width = "20%" },
}
};
section.Pane.ElementList.Add(table);

section.Footer.Height = "1cm";
section.Footer.ElementList.Add(new Text { Value = "Page {Page} of {TotalPages}" });

var data = new DocumentData();
data.Add("InvoiceNumber", "INV-00042");

var lines = new DocumentDataTable("Lines");
lines.Rows.Add(new DocumentDataTableData(new Dictionary<string, string>
{
{ "Item", "Widget" }, { "Qty", "2" }, { "Price", "10.00" }
}));
data.Add(lines);

var props = new DocumentProperties
{
Title = "Invoice INV-00042",
Author = "Acme Corp"
};

var pdf = new Renderer(new Template(section), data, documentProperties: props)
.GetPdfBinary(PageSize.A4);

File.WriteAllBytes("invoice.pdf", pdf);
```
Loading
Loading