NETReporter 3.1.1
NETReporter
Knihovna pro generování reportů (PDF, SVG, HTML, JSON) z deklarativních JSON šablon a JSON dat.
Šablona popisuje stránku, sekce, rozvržení, styly a bindingy na data; knihovna ji zvaliduje, vyhodnotí nad daty a vykreslí. PDF a SVG kreslí QuestPDF, grafy OxyPlot.
Instalace
Balíček je v privátním NuGet feedu Advisor Software:
dotnet nuget add source https://nuget.advisor-soft.com/v3/index.json --name AdvisorSoft
dotnet add package NETReporter
Požadavky: .NET 10.
Rozhraní (IReportRenderer, ITemplateTools) a sdílené typy (RenderOptions, diagnostiky…) jsou v balíčku NETReporter.Abstractions, který se nainstaluje sám. Aplikace, která report jen zadává a vykreslí ho ReportServer, použije místo této knihovny tenký klient NETReporter.Client (bez QuestPDF a dalších těžkých závislostí; přihlášení API tokenem): viz docs/user/pouziti-knihovny.md a průvodce klientem v repozitáři.
Rychlý start
using NetReporter;
var renderer = new ReportRenderer();
await using var template = File.OpenRead("template.json");
await using var data = File.OpenRead("data.json");
await using var pdf = await renderer.RenderAsync(template, data); // výchozí formát je PDF
await using var file = File.Create("report.pdf");
await pdf.CopyToAsync(file);
S DI kontejnerem (ReportRenderer se registruje jako singleton):
services.AddNetReporter(); // ReportRenderer, TemplateSchemaParser, TemplateAnalyzer, VisualTreeBuilder
services.AddNetReporter<MyPipelineObserver>(); // + vlastní IReportPipelineObserver
var renderer = serviceProvider.GetRequiredService<ReportRenderer>();
RenderAsync bere šablonu a data jako Stream a vrací Stream s výstupem (pozice 0). Při neplatné šabloně vyhodí InvalidOperationException s diagnostikami (zpráva + JSON cesta).
Šablona v kostce
{
"version": "1.0",
"meta": { "name": "Faktura", "culture": "cs-CZ", "palette": { "primary": "#1A73E8" } },
"page": { "size": "A4", "orientation": "Portrait", "marginsMm": { "top": 15, "right": 12, "bottom": 15, "left": 12 } },
"styles": {
"base": { "font": "Arial", "fontSize": 9 },
"h1": { "fontSize": 16, "bold": true, "color": "primary" }
},
"content": {
"root": {
"type": "Stack",
"gapMm": 3,
"items": [
{ "type": "Text", "text": "Faktura {{$.Number}}", "styleRef": "h1" },
{ "type": "Text", "text": "Vystaveno: {{$.Issued:dd.MM.yyyy}}, celkem {{$.Total:C2}}" },
{
"type": "Table",
"columns": [
{ "key": "name", "header": "Položka", "width": "3*" },
{ "key": "price", "header": "Cena", "width": "1*", "align": "Right" }
],
"rows": {
"source": "$.Items",
"as": "item",
"cells": {
"name": { "text": "{{item.Name}}" },
"price": { "text": "{{item.Price:N2}}" }
}
}
}
]
}
},
"pageFooter": {
"root": { "type": "PageNumbers", "mode": "Current", "showTotal": true, "separator": " / " }
}
}
- Sekce:
pageBackground,pageHeader,reportHeader,content,reportFooter,firstPageFooter,pageFooter,pageOverlay. - Vodoznak a razítko: kořenové
watermark(text nebo obrázek, podmínkavisibleIf, průhlednost v barvě) na každé stránce PDF, SVG, HTML i JSON; z kóduRenderOptions.Watermark. - Rozvržení:
Stack,Row,Box,Absolute,Spacer,PageBreak; libovolný uzel lze opakovat nad kolekcí přes"repeat": { "source": "$.Items", "as": "item" };"keepTogether": true(uStack,Row,Box,Text,Paragraph,Table) zabrání zalomení uzlu přes stránku – nevejde-li se na zbytek stránky, přesune se celý na další. - Obsah:
Text,Paragraph,HttpLink,Image,Line,PageNumbers,Barcode,Code2D(QR, DataMatrix…),PaymentQr(QR platba SPAYD a EPC s kontrolou IBAN),Checkbox,Emoji(barevné emoji Twemoji jako vektor),Icon(ikony FontAwesome Free, s licencí i Pro, jako vektor),Table(skupiny, souhrny, stránkování),Chart. - Binding:
{{$.Cesta.K.Hodnote}}(kořen dat),{{alias.Pole}}(položkarepeat/tabulky/grafu), formát za dvojtečkou ({{$.Total:N2}},{{$.Date:dd.MM.yyyy}}), C# výrazy s prefixem#({{#item.Qty * item.Price}}), systémové proměnnénow,page.number,page.total. Formátuje se podlemeta.culture. - Styly: pojmenované styly v
styles(baseplatí pro vše), odkazstyleRefa inlinestyle; písmo, barvy (#RRGGBBnebo název zmeta.palette), okraje a výplně v mm, rámečky, zarovnání. Styl lze vybrat podle dat –styleRefmůže obsahovat binding, jehož výsledkem je jméno stylu ("styleRef": "{{# item.Overdue ? \"overdue\" : \"\" }}"); tabulky mají styl řádkurows.styleRefa buňkyrows.cells.<key>.styleRef/style.
Kompletní popis formátu je v přibalené dokumentaci (viz níže) – kapitola 02_template_format.md.
Grafy
{
"type": "Chart",
"chartType": "Column",
"source": "$.Months",
"as": "m",
"category": "{{m.Month}}",
"series": [
{ "name": "2025", "value": "m.Revenue", "color": "primary" },
{ "name": "Plán", "value": "#m.Revenue * 1.1", "dashed": true }
],
"valueAxis": { "format": "N0" },
"legend": { "position": "Top" }
}
Typy Column, Bar, Line, Area, Pie, Donut; více sérií, paleta, legenda, osy, popisky hodnot, skládání. Kombinovaný graf: série může přepsat typ ("type": "Line") a čáry mohou mít druhou osu vpravo ("axis": "Secondary" + secondaryValueAxis). Grafy jsou vektorové ve všech výstupech. Podrobnosti: 02_template_format.md §7.11 a 17-charts.md.
Výstupní formáty
await using var output = await renderer.RenderAsync(template, data, new RenderOptions { Format = RenderFormat.Svg });
| Formát | Popis |
|---|---|
RenderFormat.Pdf |
výchozí; plnohodnotné PDF se stránkováním |
RenderFormat.Svg |
stejný layout a stránkování jako PDF; jeden SVG se stránkami pod sebou (data-pages, <svg data-page="N">) |
RenderFormat.Html |
HTML náhled bez stránkování (vhodný pro editor/prohlížeč) |
RenderFormat.Json |
strukturovaný výsledek renderu – vyhodnocené texty, data grafů, metadata obrázků a kódů; pro automatické a AI kontroly |
RenderFormat.Webp |
obrázek stránky se stejným layoutem jako PDF (ztrátový, bílé pozadí); RenderAsync vrací první stránku, všechny RenderWebpPagesAsync |
RenderFormat.Png |
obrázek stránky bezeztrátově (bílé neprůhledné pozadí); RenderAsync vrací první stránku, všechny RenderPngPagesAsync |
RenderFormat.Jpeg |
obrázek stránky ztrátově, bez průhlednosti; RenderAsync vrací první stránku, všechny RenderJpegPagesAsync |
Každou SVG stránku zvlášť vrací RenderSvgPagesAsync:
IReadOnlyList<string> pages = await renderer.RenderSvgPagesAsync(template, data);
Do SVG se nevkládají fonty – prohlížeč použije písmo nainstalované v systému (pozice znaků zůstanou zachované).
Každou stránku jako obrázek WebP vrací RenderWebpPagesAsync (rozlišení RenderOptions.ImageDpi 1–600, výchozí 288, kvalita ImageQuality; všechny stránky jsou najednou v paměti):
IReadOnlyList<byte[]> pages = await renderer.RenderWebpPagesAsync(template, data, new RenderOptions { ImageDpi = 150 });
Stejně funguje RenderPngPagesAsync (bezeztrátově, ImageQuality se ignoruje) a RenderJpegPagesAsync (ztrátově, kvalita ImageQuality).
Ladění šablon: new RenderOptions { Format = RenderFormat.Html, RenderBindingsAsExpressions = true } zobrazí místo hodnot samotné binding výrazy. RenderOptions.DebugMode přidá k HTML výstupu ladicí atributy (data-nr-type s typem uzlu u prvků s id), ladicí CSS a skript pro vizuální editor šablon; výstup bez něj se nemění.
Limit stránek: PDF/SVG render skončí RenderPageLimitExceededException po překročení RenderOptions.MaxPages (výchozí 10 000, 0 = bez limitu) – ochrana před šablonou se zacykleným layoutem. CancellationToken se kontroluje i během generování stránek. Všechny formáty se renderují souběžně: od QuestPDF 2026.9 je souběžné generování PDF/SVG v jednom procesu bezpečné a ReportRenderer ho neserializuje, takže propustnost roste s počtem vláken.
Metadata PDF
Vlastnosti dokumentu (Název, Autor, Předmět, Klíčová slova, Aplikace, Jazyk) se nastavují v meta.document. Hodnoty jsou texty s bindingy vyhodnocené nad daty; prázdný výsledek se nezapíše, klíčová slova mohou být pole a bez language se použije meta.culture. Metadata se zapisují jen do PDF.
"meta": {
"name": "Faktura",
"culture": "cs-CZ",
"document": { "title": "Faktura {{$.Number}}", "author": "{{$.Supplier}}", "keywords": [ "faktura", "{{$.Number}}" ] }
}
Podrobnosti: 02_template_format.md §1.4.
Profesionální PDF
Hlavičkový papír, připojená PDF, PDF/A, přílohy a šifrování se doplňují do PDF po vygenerování (post-processing přes qpdf z balíčku QuestPDF; šablony bez těchto funkcí dávají stejné PDF jako dřív).
Hlavičkový papír a připojená PDF (šablona; fragment): page.letterhead vloží stránku PDF pod obsah stránek, meta.document.append připojí PDF za dokument. Zdroj je cesta k souboru, http(s) URL nebo data URI, nejčastěji {{$.Assets.Název}}:
"meta": { "assets": { "Hlavicka": "data:application/pdf;base64,JVBERi0…" },
"document": { "append": [ "https://firma.cz/vop.pdf" ] } },
"page": { "size": "A4", "orientation": "Portrait", "marginsMm": { "top": 40, "right": 15, "bottom": 25, "left": 15 },
"letterhead": { "src": "{{$.Assets.Hlavicka}}", "pages": "FirstAndRest" } }
pages: All (výchozí), First, FirstAndRest (první stránka 1. stránkou podkladu, ostatní 2. stránkou). Nedostupný zdroj render nezastaví: PDF vznikne bez něj a RenderOptions.OnBindingDiagnostics (nebo pozorovatel) dostane varování NRP036 s JSON cestou zdroje; kontrolujte je, než fakturu odešlete. Server, který renderuje cizí šablony, má nastavit ResourcePolicy = ResourcePolicy.Server (zdroje ze souborů zakázané, stahování jen z veřejných adres, limity velikosti a času).
PDF/A-3b s přílohou (ISDOC), vstupní data v PDF a heslo:
var options = new RenderOptions
{
PdfConformance = PdfConformance.PdfA3B,
Attachments = [ IsdocWriter.CreateAttachment(isdocInvoice) ], // ISDOC z dat faktury (NetReporter.Isdoc), nebo vlastní new PdfAttachment { ... }
EmbedData = true, // vstupní JSON jako data.json (null = podle meta.document.embedData)
};
// šifrování (AES-256) nejde spojit s PDF/A
var secured = new RenderOptions { Encryption = new PdfEncryption { UserPassword = "ctenar", OwnerPassword = "vlastnik", AllowModification = false } };
PDF/A-2b přílohy nepovoluje. Volby PDF u jiného formátu než PDF a neslučitelné volby jsou ArgumentException. IsdocWriter složí ISDOC 6.0.2 z IsdocInvoice (součty a rekapitulaci DPH dopočítá, UUID je stabilní, výstup je ověřený proti oficiálnímu XSD); PDF/A ověřte nástrojem veraPDF. Podrobnosti: user/pouziti-knihovny.md a dev/07_renderer_questpdf.md.
Validace šablony
Šablonu lze zkontrolovat bez renderu. Parser ji ověří proti vestavěnému schématu a vrátí diagnostiky s JSON cestou k chybě:
using NetReporter.Common;
using NetReporter.Template.Parsing;
var parser = new TemplateSchemaParser(); // výchozí ParserValidationMode.Strict
var result = parser.Parse(templateJson);
if (!result.Success)
{
foreach (var d in result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
Console.WriteLine($"{d.JsonPath}: {d.Message} ({d.Code})");
}
Stricthlásí neznámé vlastnosti a chybný obsah stylů (neznámá vlastnost, špatný typ, neplatný enum) jako chybu,Compatjako varování (new TemplateSchemaParser(validationMode: ParserValidationMode.Compat); při renderunew RenderOptions { ValidationMode = ParserValidationMode.Compat }, varování pak dostane pozorovatel vParseCompletedContext.Diagnostics).- Diagnostiky mají stabilní
CodeaJsonPath(např.$.content.root.items[2].rows.source). - Schéma šablon (typy uzlů, vlastnosti, enumy) vrací
TemplateSchemaProvider.Create().
Podrobnosti: kapitola 03_parser_and_validation.md.
Data, která šablona čte
Chybějící datové pole se vykreslí prázdné a nikdo o tom nezjistí. Analyzátor zjistí ze šablony (bez renderu), která pole dat čte, a složí z nich JSON Schema nebo ukázková data:
using NetReporter.Template.DataContract;
var contract = new TemplateDataContractAnalyzer().Analyze(templateJson);
foreach (var field in contract.EnumerateFields())
Console.WriteLine($"{field.Path} {field.Kind}"); // $.Invoice.Items[*].Total Number
string schema = contract.ToJsonSchema(); // JSON Schema 2020-12, bez povinných polí
string sample = new TemplateSampleDataGenerator().GenerateJson(contract); // deterministická ukázková data
Druh pole se odvodí z použití, formátu a názvu (názvový odhad kontrakt označí); C# výrazy {{# …}} se čtou jen podle zápisu. Podrobnosti: user/pouziti-knihovny.md a kapitola 05_binding_system.md.
Sledování pipeline
IReportPipelineObserver dostává události OnPipelineStarted, OnParseStarted/Completed, OnRenderStarted/Completed (doba, velikost výstupu) a OnError (fáze, kategorie, stabilní kód chyby) – vhodné pro metriky a logování. Registruje se přes AddNetReporter<TObserver>() nebo konstruktor ReportRenderer. Knihovna loguje přes ILogger, pokud předáte ILoggerFactory.
RenderCompletedContext v OnRenderCompleted nese také BindingDiagnostics a OmittedBindingDiagnostics: chyby v bindingech (nedokončené {{, nečíselná hodnota grafu…) render nezastaví, takže je jinak neuvidíte. Shodné diagnostiky se slučují a počet omezuje RenderOptions.MaxBindingDiagnostics (výchozí 200, 0 sběr vypne). Při selhání renderu je (do chyby nasbírané) nese i PipelineError v OnError.
Dokumentace v balíčku
Balíček obsahuje dokumentaci knihovny a šablon ve složce docs/ (po instalaci v %USERPROFILE%\.nuget\packages\netreporter\<verze>\docs\, na Linuxu ~/.nuget/packages/netreporter/<verze>/docs/). Rozcestník je docs/README.md:
| Soubor | Obsah |
|---|---|
docs/user/pouziti-knihovny.md |
průvodce použitím knihovny krok za krokem |
docs/dev/02_template_format.md |
formát šablony – sekce, uzly, vlastnosti, repeat, tabulky, grafy |
docs/dev/03_parser_and_validation.md |
parser a validace šablon, diagnostiky |
docs/dev/04_style_system.md |
stylovací systém |
docs/dev/05_binding_system.md |
binding a výrazy |
docs/dev/17-charts.md |
grafy – chování a omezení |
docs/dev/18-emoji-a-ikony.md |
emoji a ikony FontAwesome – vestavěné sady, Pro a stahování verzí, licence |
docs/dev/01_introduction.md, 06_runtime_pipeline.md, 07_renderer_questpdf.md, 08_program_flow_example.md, 09_extensibility.md |
architektura knihovny, pipeline, renderer, rozšiřitelnost |
Seznam změn všech verzí je v souboru CHANGELOG.md v kořeni balíčku; změny konkrétní verze ukazuje NuGet v release notes balíčku.
Licence
MIT, © Advisor Software s.r.o.
Zdrojový kód, ukázkové šablony a změny verzí: repozitář projektu.
No packages depend on NETReporter.
.NET 10.0
- NETReporter.Abstractions (>= 3.1.1)
- Microsoft.CodeAnalysis.CSharp.Scripting (>= 4.14.0)
- Microsoft.Extensions.Configuration.UserSecrets (>= 6.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- OxyPlot.Core (>= 2.2.0)
- QuestPDF (>= 2026.9.1)
- ZXing.Net (>= 0.16.9)
| Version | Downloads | Last updated |
|---|---|---|
| 3.9.2 | 3 | 10/28/2026 |
| 3.9.1 | 1 | 10/27/2026 |
| 3.9.0 | 6 | 10/20/2026 |
| 3.8.1 | 6 | 10/19/2026 |
| 3.8.0 | 4 | 10/19/2026 |
| 3.7.0 | 4 | 10/19/2026 |
| 3.6.0 | 6 | 10/17/2026 |
| 3.5.1 | 62 | 10/15/2026 |
| 3.5.0 | 4 | 10/15/2026 |
| 3.4.2 | 5 | 10/15/2026 |
| 3.4.0 | 4 | 10/12/2026 |
| 3.3.0 | 5 | 10/11/2026 |
| 3.2.2 | 5 | 10/10/2026 |
| 3.2.1 | 4 | 10/10/2026 |
| 3.2.0 | 4 | 10/10/2026 |
| 3.1.1 | 4 | 10/09/2026 |
| 3.1.0 | 5 | 10/09/2026 |
| 3.0.1 | 8 | 10/08/2026 |
| 3.0.0 | 5 | 10/08/2026 |
| 2.2.1 | 5 | 10/05/2026 |
| 2.2.0 | 4 | 10/05/2026 |
| 2.1.0 | 9 | 10/04/2026 |
| 2.0.1 | 8 | 10/04/2026 |
| 2.0.0 | 5 | 10/03/2026 |
| 1.7.0 | 6 | 10/03/2026 |
| 1.6.0 | 5 | 10/02/2026 |
| 1.5.0 | 12 | 10/02/2026 |
| 1.4.0 | 6 | 10/01/2026 |
| 1.3.0 | 4 | 10/01/2026 |
| 1.2.0 | 5 | 10/01/2026 |
| 1.1.1 | 6 | 09/30/2026 |
| 1.1.0 | 8 | 09/30/2026 |
| 1.0.10 | 4 | 09/29/2026 |
| 1.0.9 | 4 | 09/29/2026 |
| 1.0.8 | 16 | 09/29/2026 |
| 1.0.7 | 115 | 09/24/2026 |
| 1.0.6 | 9 | 09/24/2026 |
| 1.0.5 | 7 | 09/24/2026 |
| 1.0.4 | 11 | 09/24/2026 |
| 1.0.3 | 8 | 09/24/2026 |
| 1.0.2 | 13 | 09/23/2026 |
| 1.0.0 | 7 | 09/23/2026 |
| 0.9.1 | 13 | 09/23/2026 |
| 0.9.0 | 87 | 09/14/2026 |
| 0.8.1 | 25 | 02/20/2026 |
| 0.8.1-preview.1 | 34 | 02/13/2026 |
| 0.7.1-preview.2 | 32 | 02/10/2026 |
| 0.7.1-preview.1 | 33 | 02/10/2026 |
| 0.7.0 | 30 | 02/10/2026 |
| 0.0.6 | 28 | 02/01/2026 |
| 0.0.5 | 19 | 02/01/2026 |
| 0.0.0-alpha.0.17 | 35 | 02/01/2026 |