NETReporter 2.1.0

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ínka visibleIf, průhlednost v barvě) na každé stránce PDF, SVG, HTML i JSON; z kódu RenderOptions.Watermark.
  • Rozvržení: Stack, Row, Box, Absolute, Spacer, PageBreak; libovolný uzel lze opakovat nad kolekcí přes "repeat": { "source": "$.Items", "as": "item" }; "keepTogether": true (u Stack, 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žka repeat/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 podle meta.culture.
  • Styly: pojmenované styly v styles (base platí pro vše), odkaz styleRef a inline style; písmo, barvy (#RRGGBB nebo název z meta.palette), okraje a výplně v mm, rámečky, zarovnání. Styl lze vybrat podle dat – styleRef může obsahovat binding, jehož výsledkem je jméno stylu ("styleRef": "{{# item.Overdue ? \"overdue\" : \"\" }}"); tabulky mají styl řádku rows.styleRef a buňky rows.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á zakázat zdroje ze souborů: AllowLocalPdfSources = false.

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})");
}
  • Strict hlásí neznámé vlastnosti a chybný obsah stylů (neznámá vlastnost, špatný typ, neplatný enum) jako chybu, Compat jako varování (new TemplateSchemaParser(validationMode: ParserValidationMode.Compat); při renderu new RenderOptions { ValidationMode = ParserValidationMode.Compat }, varování pak dostane pozorovatel v ParseCompletedContext.Diagnostics).
  • Diagnostiky mají stabilní Code a JsonPath (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.

- Template editor: selecting a node in the structure tree while the JSON tab is open now marks the whole node in the text when it is short (up to 12 lines) and, for a longer node, its header from the opening brace (or the property name, `"root": {`) down to the line with `type` or `id`; before only the first line was marked, often a lone `{`. - Samples: the new template `ceska-faktura-ikony` (data `ceska-faktura-ikony.sample-data`) is the Czech tax invoice with FontAwesome icons: a colored title band with the document number, cards of the supplier and the customer with icons (an envelope at the e-mail, phone, web, address, company id; the order, the final recipient and the tax id of the customer only when the data has them), tiles with the dates and the state of the payment, the items table, the payment details with the QR payment, the VAT recap, the total and the fields for the signatures and the stamp. A paid invoice (`Invoice.Paid`) shows the confirmation of the payment instead of the payment details and gets the watermark; `meta.document.isdoc` attaches the ISDOC to PDF/A-3b. It reads the same data as `ceska-faktura`. - Template editor: in the structure tree, the highlighted row of the selected and of the hovered node sat about 9 px above the text of the node (the Syncfusion theme gives the node text `min-height: 40px` and a padding of 10 px, while the row and its background are 22 px); the text has the height of the row now. The selected node has a darker background in the light and in the dark theme. - Template editor: the new last tab **ISDOC** edits `meta.document.isdoc` in a form (document, supplier, customer, lines, VAT recap, payment) with a pick list of the data fields (the preview data and the data contract of the template) for every value, shows the ISDOC composed from the form and the preview data (XML, download of `{number}.isdoc`) and checks it against the official schema `isdoc-invoice-6.0.2.xsd` (embedded in the editor); problems of the parser, of the data (`NRP042`) and of the schema are shown at the field they belong to. Properties the editor does not know stay in the template. - Template editor: the folder used without a database is configurable - `TemplatesPath` or `EDITOR_TEMPLATES_PATH` (templates, the `sampleData` folder and the user snippets in `snippets` live inside it); the default stays `~/Documents/ReportTemplates`. k3s: new `deploy/k8s/02-pvc.yaml` (PersistentVolumeClaim `netreporter-editor-data`, 1 Gi) mounted to `/data/templates` of the editor Deployment, which now uses the `Recreate` strategy because the volume is ReadWriteOnce; without it the templates saved to the folder of the pod vanished with every rollout. The Docker Compose sample has the volume `editor-data`. - ReportServer, editor and shared accounts: package updates within .NET 10 - EF Core 10.0.12, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, ASP.NET Core packages (Identity, DataProtection, JwtBearer, Google and Microsoft sign-in) 10.0.12, `Microsoft.Extensions.*` 10.0.12 in the server core and System.IdentityModel.Tokens.Jwt 8.23.0. `Microsoft.EntityFrameworkCore.Design` (the tool for migrations) stays on 10.0.0, because 10.0.12 brings Roslyn 5 that clashes with Roslyn 4.14 used for the C# expressions of the library (NU1608). The library packages `NETReporter`, `NETReporter.Abstractions` and `NETReporter.Client` keep their wide dependency ranges. - **Breaking** ReportServer UI: HealthChecks UI (`/ui/health-ui`, `/ui/health-ui-api`) is replaced by an own status page `/ui/status` (the link "Stav" in the navigation, for every signed-in user): the state of the UI and the API with its response time, the API components from `/healthz` (database, storage, accounts) with their descriptions, the history of the last 20 checks and a refresh every 15 s. The package `AspNetCore.HealthChecks.UI` 9.0.0 is the latest release, it pulled EF Core InMemory 8 and did not fit EF Core 10 of the shared accounts; the packages `AspNetCore.HealthChecks.UI*`, `KubernetesClient`, `IdentityModel` and `Microsoft.EntityFrameworkCore.InMemory` of the UI are removed. The probes `/livez`, `/readyz` and `/healthz` stay. - ReportServer: deleting an account (`DELETE /users/{id}`, the button in the shared user administration) also deletes the editor templates and snippets of that account (`editor."Templates"` and `"Snippets"`), which would otherwise stay as orphans. The API finds the editor database in the same variable as the editor (`EDITOR_CONNECTION_STRING`, `ConnectionStrings:EditorDb`; in k3s every service has it from the shared Secret); without it nothing is deleted. An unavailable editor database does not stop the deletion of the account (a warning in the log, `EditorCleanup=failed` in the application log entry). - **Breaking** ReportServer: the old user tables `public."Users"` and `public."UserRoles"` are dropped. The new Identity migration `DropLegacyReportServerUsers` runs right after the import `ImportLegacyReportServerUsers` and drops them only when every old user has a shared account with the same id; otherwise the tables stay unchanged (the database logs a warning) so no user is lost. The ReportServer model no longer knows the tables (the no-op migration `RemoveLegacyUsers`), the classes `ReportUser`, `UserRole` and `PasswordHashUtility` are gone (the password hasher of the shared accounts still verifies the old PBKDF2 format) and `DELETE /users/{id}` no longer touches the old tables. Returning to a version before the shared accounts (1.x) now needs the database restored from a backup, so take a `pg_dump` before the first start. - ISDOC outside the render: `ITemplateTools.ComposeIsdocAsync(template, data)` composes the ISDOC of `meta.document.isdoc` without rendering a PDF (`TemplateIsdocResult`: file name, content, template and binding diagnostics; a problem is a result with `NRP042` and the JSON path, not an exception). The default interface implementation throws `NotSupportedException`; `TemplateTools` and `ReportServerTemplateTools` implement it. ReportServer: new endpoint `POST /template-tools/isdoc` and the render option `embedIsdoc` (`RenderOptionsRequest.EmbedIsdoc`, mapped by the client, part of the cache key). MCP: the new tool `compose_isdoc` (XML text, file name, diagnostics with JSON paths), a step in the server instructions and in the prompt `design_report_template`. ReportServer UI: the render page has the select "ISDOC ze šablony" (embed when the template has the section / do not embed) and the button "Složit ISDOC" for a stored and an ad-hoc template (download of the XML, the reasons of a failure with JSON paths). - ISDOC from the template data: the new section `meta.document.isdoc` of a template describes how to compose an electronic invoice (ISDOC 6.0.2) from the input data with bindings: document number and dates, supplier and customer, `lines` (a data path, an alias and the line values), an optional `vatRecap` and `payment`. A PDF render embeds it as an attachment (`{number}.isdoc`, relationship Alternative; PDF/A-3b recommended), the totals and the VAT recap are computed bottom-up from the lines. New option `RenderOptions.EmbedIsdoc` (`null` = by the template, `false` = never, `true` = always; `true` with PDF/A-2b is an `ArgumentException`); with PDF/A-2b the ISDOC from the template is skipped with `NRP037`. A document that cannot be composed (a missing or unreadable required value, lines that are not an array, a document ISDOC does not allow) is not embedded and the PDF is rendered without it with the new warning `NRP042` (`DiagnosticCodes.IsdocUnavailable`) and the JSON path of the value. The section is validated by the parser (`NRP024` warnings), its bindings are part of the data contract and the editor JSON schema knows it. The sample `ceska-faktura` has an ISDOC section whose totals match the printed ones. - Template editor: the parser errors and warnings (`NRP…`) of the template are underlined in the JSON tab while typing, about 0.8 s after the last keystroke, instead of only after switching the tab or "Parsovat a zobrazit strom". The check only reads (no entry in the undo history, no change of the tree or the text); templates over 300 000 characters are still checked only on analysis. - Icons: an icon pack loaded from disk or built from the FontAwesome metadata keeps the path data of the shapes as UTF-8 and builds `IconShape` only when a variant is read (`IconDefinition.Variants` stays a read-only dictionary with the same contents). The paths of the bundled Free pack take half the memory (2.8 → 1.4 MB, the whole pack 5.4 → 4.0 MB); a Pro pack (64 000 shapes) should drop from about 100 MB to about 50 MB. Looking up an icon costs about 1.6 µs. - ReportServer UI: the render page offers the output format PDF, PNG, JPEG or WebP and the image resolution (DPI). An image result (the first page, as the API returns it) is shown inline with a download link (`report.png`, `report.jpg`, `report.webp`); a PDF result shows the page count (`X-Render-Page-Count`). The page options error (resolution, password) no longer leaves the previous result next to the message. The templates page has a **preview** of the first page (eye button in the list and in the editor, including unsaved edits): sample data are generated from the data contract of the template (`/template-tools/sample-data`) and the page is rendered ad hoc as WebP; it is a normal render job with the format `webp`. - Template editor (database storage): templates and snippets can be **published**. A globe icon on a card in "Load template" (and in "My snippets") makes an own template or snippet visible to every signed-in user, a lock takes it back; publishing changes only the visibility, not the content, revision or thumbnail. The new filter "Veřejné" lists the public templates of other users (opened by their `Id`, since names are unique per owner only): they open read-only and saving creates an own copy, the original stays untouched. Own templates can be deleted from the gallery (trash icon, confirmed). A public snippet of another user has its own menu key (`shared:`), so it no longer clashes with an own snippet of the same id. The folder storage does not know visibility (`ITemplateStore.SupportsVisibility`, `ISnippetStore.SupportsVisibility` are `false`). - ReportServer API: a JWT stops being valid once the account is deactivated, its password changed or a role removed, instead of at expiry. The token carries the security stamp of the account (`nrp_stamp`) and the API compares it with the current one (the account is read at most every `Jwt:RevalidationSeconds`, default 30 s, env `JWT_REVALIDATION_SECONDS`, `0` = every request); a mismatch gives `401`. Tokens issued before this change have no stamp and are valid until they expire. API tokens (`nrp_…`) were checked on every request already. - ReportServer UI: the render page has "Volby výstupu PDF" for both renders: watermark, PDF/A (2b, 3b), embedding of the input data as `data.json` (by template / always / never) and AES-256 encryption (owner password, optional password to open; the passwords are neither stored nor logged). Attachments are not offered in the UI and the passwords are not offered in MCP on purpose. - Template editor: the page tab has the card "Podklad a přílohy (jen PDF)": the letterhead source and pages (`page.letterhead`), the appended PDFs (`meta.document.append`, one per line) and the switch that embeds the input data (`meta.document.embedData`). Other properties of the letterhead and of `meta.document` are kept; a value of another shape is left unchanged and edited in the JSON tab. - Samples: the group header and the group summary of `benchmark-extreme` read `{{group}}` and `{{groupSums.kN}}` instead of the row alias `r` that is not visible there (the header used to render as "Skupina ", the summary was empty). - Accounts: the new page `Account/Manage/ExternalLogins` ("Propojené účty", linked from the account menu when Google or Microsoft sign-in is on) lists the external accounts an account is linked to and unlinks them; an account without a password cannot unlink its only sign-in. The administrator can unlink a provider from the user list too (the action goes to the audit, `ExternalLoginRemoved`). Signing in through the provider links the account again. - Accounts: the keys of ASP.NET Core Data Protection that sign the shared sign-in cookie can be encrypted with a certificate: `NETREPORTER_AUTH_DATAPROTECTION_CERTIFICATE` (PFX in base64) or `_CERTIFICATE_PATH`, with `_CERTIFICATE_PASSWORD`; `_PREVIOUS_CERTIFICATE` keeps old keys readable while a certificate is replaced. Without it the keys in `identity."DataProtectionKeys"` stay plain text as before. The certificate must be the same in the template editor and the ReportServer UI; an invalid or expired one stops the start with a clear error. In k3s the four secret keys are optional. Procedure and rotation: server chapter 21. - Template editor: the dialog "Moje bloky" edits the name, category and description of an own snippet, **exports** a snippet to a file (`{id}.snippet.json`, format `netreporter-snippet`) and **imports** such a file (up to 1 MB; the id comes from the name, so an import never overwrites a snippet; a snippet with node types this version does not know is imported as inactive), so snippets can be shared between instances. - MCP: the bundled template snippets and themes of the editor (`snippets`, `themes`) are available to agents: the tools `list_snippets`, `get_snippet`, `list_themes` and `get_theme` and the resources `netreporter://snippets/{id}` and `netreporter://themes/{id}` (also listed in `resources/list`). The server instructions and the prompt `design_report_template` point to them. - ReportServer API: failed authentications with an API token (unknown, revoked or expired token, inactive owner) are counted per client address (`ApiTokenFailureLimiter`, the same count and window as the sign-in limiter: `Security:RateLimit:AuthPermitLimit` and `WindowSeconds`). A client that uses up the limit gets `429` with `Retry-After` on requests carrying `Bearer nrp_…` before any database query, so the database cannot be flooded with made-up tokens; valid tokens do not use the limit. Behind a proxy the address needs `ASPNETCORE_FORWARDEDHEADERS_ENABLED=true`. - ReportServer: a render job records the API token that started it (`RenderJob.ApiTokenId`, migration `AddRenderJobApiTokenId`, no foreign key). `ApiTokenId` is in the job detail and list; `GET /auth/tokens` and `GET /admin/api-tokens` carry `RenderCount` and `LastRenderUtc` by jobs (the approximate `LastUsedUtc` stays); the UI shows the token in the job list and detail and the render count in the token tables. - ReportServer: `POST /render/by-template/{id}` accepts `options` like the ad-hoc render (watermark, PDF/A, attachments, encryption, format…); the UI render page has a watermark field for both renders and the MCP tool `render_template` the parameter `watermark`. - ReportServer: user-management actions done on the shared account pages (`Account/Admin/*`: create, activate or deactivate, roles, password) are written to the application log like the same actions through the API: the new `IUserAdministrationAudit` (implemented by the ReportServer UI through `POST /admin/user-audit`; the template editor leaves them in the process log). The password never gets there. - Accounts: e-mails (confirmation, password reset) are sent with MailKit, so a provider with only port 465 works: with `NETREPORTER_AUTH_SMTP_ENABLE_SSL` (default `true`) the connection uses implicit TLS on port 465 and STARTTLS on other ports. - MCP: the tools `validate_template`, `validate_data`, `validate_template_file`, `validate_data_files`, `get_data_contract` and `generate_sample_data` call `ITemplateTools` of the library instead of their own copy of the logic (the same result as in a local application and on the ReportServer); `validate_data` caps the list at 50 diagnostics as before, reads no local files for PDFs or images, and describes the sections `pageBackground`, `firstPageFooter` and `pageOverlay` correctly (JSON evaluates them). - ReportServer UI: the data types of the API come from the shared project `NetReporter.ReportServer.Contracts` instead of hand-written copies. - Fix: PDF text styles pass **all** entries of `fontFallbacks` to QuestPDF (`font` first, then the fallbacks in order), no longer only the first one, so `["Noto Emoji", "Segoe UI Symbol"]` really tries both (ToDo #100). - ReportServer API: a rejected request (`429 RATE_LIMIT_EXCEEDED`) carries the header `Retry-After` (the limiter window in seconds, exposed in CORS). `NETReporter.Client` reads it into the new `ReportServerException.RetryAfter` (seconds or HTTP date) and names the wait in the message; the MCP client of the ReportServer API does the same (`ReportServerApiException.RetryAfter`). - MCP: `render_report` and `render_report_from_files` accept `format=png` (lossless) and `format=jpeg` (alias `jpg`, lossy) next to `webp`: one page as an image (`page`, `dpi`), shown inline as an image, with the MIME type `image/png` or `image/jpeg` and the extension `.png` or `.jpg`. The tool descriptions no longer say that HTML and JSON skip `pageBackground`, `firstPageFooter` and `pageOverlay` (they have rendered them since 1.7.0). - MCP: the connection to the ReportServer API can use an API token (`nrp_…`, `ReportServer:ApiToken`, env `MCP_REPORTSERVER_API_TOKEN`) instead of the e-mail and password of the service account: the token is sent as `Authorization: Bearer` without a login and without refreshing a JWT, and it can be revoked in the ReportServer UI without changing a password. The token takes precedence; the e-mail and password are needed only without it. In k3s the key `MCP_REPORTSERVER_API_TOKEN` is optional, and so is now `MCP_REPORTSERVER_PASSWORD` (at least one of them must be set). - Template editor: the page tab edits the watermark options `pages` (checkbox "Jen první stránka" = `First`) and `behind` ("Pod obsahem"); the table editor edits the condition of a column (`columns[].visibleIf`, "Zobrazit, když"); the chart editor edits `filter` and `sortBy` of the chart rows. The outdated hint under the HTML preview that `pageBackground`, `firstPageFooter` and `pageOverlay` are not rendered is gone (the preview draws them as page layers). - Samples: the sample templates and sample data (`sampleTemplates`, `sampleData`) are anonymized. The company logo in `invoice`, `nda`, `odesilacilist` and `prohlaseni-mlcenlivost` and in the console sample (`Assets/logo.png`) is a neutral placeholder, and the company names, addresses, company ids, contacts, people, internal form numbers and the personal data of the `eldp-formula` sample are fictional (`Vzorová firma s.r.o.`, `87654321`, `example.com`). The layout and the data contracts of the samples are unchanged. Celý seznam změn: CHANGELOG.md v balíčku a https://dev.azure.com/advsoft/Knihovny/_git/NetReporter?path=/src/NetReporter/CHANGELOG.md

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