- HTML and JSON output: the sections `pageBackground`, `pageOverlay` and `firstPageFooter` are rendered in every format now. HTML draws them as page layers (`div.nr-page-background` and `div.nr-page-overlay` are `position:fixed` over the page, the footer sits at the bottom of the first page inside the page margins; the print CSS defines the variables `--nr-page-h` and `--nr-margin-*`), JSON writes `sections.pageBackground`, `sections.pageOverlay` and `sections.firstPageFooter` (and the entries of `documentTree`) only for a template that has them. The bindings in these sections are evaluated, so `validate_data` and the render diagnostics of `format=json` and `format=html` report their problems too.
- Templates: the local variable `loop` in the content of `repeat`, in the rows of a table and in the rows of a chart: `loop.index` (from 1), `index0`, `first`, `last`, `odd`, `even` and `count` (the count after the filter), so `{{loop.index}}.` numbers items and `rows.styleRef: "{{# loop.odd ? \"zebra\" : \"\" }}"` stripes rows. A nested loop shadows it, an alias called `loop` wins, and the data contract does not treat it as a data field.
- Templates: table columns have `visibleIf` (a condition with a binding, evaluated once per table; a hidden column is not rendered, the weights are shared by the rest, column sums can still be used; all columns hidden gives a warning and no table; the debug mode shows all columns). Charts have `filter` and `sortBy` of the rows with the rules of `repeat.filter` and `repeat.sortBy`. Warnings `NRP031` and the error `NRP032` apply to them as to `repeat` and `rows`.
- Templates: `PaymentQr` has more keys. SPAYD: `altAccounts` (`ALT-ACC`, alternative accounts with an optional BIC, at most 2), `notificationType` (`Phone` or `Email`) with `notificationAddress` (`NT` and `NTA`, written only together), `retryDays` (`X-PER`, 0-30) and `crc32` (the optional `CRC32` checksum as the last attribute: CRC-32 of the header and the other attributes sorted by key and value, 8 uppercase hex digits). EPC: `purpose` (the four-letter ISO 20022 purpose code on the ninth line). An invalid value is left out with a warning, a static one is `NRP033`; the keys of the other standard are ignored.
- Bindings: `Slovy(amount, currency, language)` knows more currencies and a second language. Currencies: `CZK` (default), `EUR`, and new `USD` (dolar), `GBP` (libra šterlinků, penny), `PLN` (zlotý, groš), `CHF` (frank, rap) and `HUF` (forint, fillér) with the Czech noun forms by the last numeral. The new third parameter `language` is `cs` (default, independent of the culture of the template) or `en` (also a culture such as `en-US`): `Slovy(1234.56, "EUR", "en")` is "one thousand two hundred thirty-four euros and fifty-six cents". Any other language or currency is an error reported as a warning with the JSON path, as before.
- Data contract: the generated sample data of an object with the numeric fields `Total` and `Vat` (a table row whose summary sums the column `vat`, or a summary object) gives `Vat` as 21 % of `Total` rounded to cents, no longer the same amount as `Total`, so the sample VAT summary differs from the total one. The contract itself is unchanged (`Vat` is listed because the summary reads it first and, like every field of the contract, it is optional).
- New output formats `RenderFormat.Png` (lossless) and `RenderFormat.Jpeg` (lossy): the same layout and paging as the PDF, like WebP. `ReportRenderer.RenderPngPagesAsync` and `RenderJpegPagesAsync` return every page as an image, `RenderAsync` with `Format = Png` or `Jpeg` returns the first page. `RenderOptions.ImageDpi` (1-600, default 288) applies to all three image formats and `ImageQuality` to the lossy ones (WebP, JPEG); PNG ignores it. The PNG file has an alpha channel, but the page background is opaque white. PDF-only options and template features are rejected or reported (`NRP035`) the same way as for WebP.
- Templates: `PageNumbers` has the new properties `prefix` and `suffix`, a text before and after the page numbers in every mode (`"prefix": "Strana "` with `"showTotal": true, "separator": " z "` writes "Strana 3 z 5"). The texts, like `separator`, may contain a binding (`"{{$.Labels.Page}} "`) and the data contract reads the fields in them. The block "Patička „Strana X z Y“" of the template editor uses it instead of a separate text column.
- Templates: `watermark` has the new properties `pages` (`All` by default, or `First` for the first page only) and `behind` (`true` puts the watermark under the content). PDF and SVG compose a first-page watermark only for page 1 and put a `behind` watermark on the page background layer above `pageBackground`; HTML puts a first-page watermark in an absolutely positioned layer of the height of the page (`--nr-page-h`), so the print does not repeat it, and a `behind` one in a layer with `z-index:-1`. The JSON output shows the watermark regardless of both.
- New: ISDOC 6.0.2 export of an invoice. `IsdocWriter` (`NetReporter.Isdoc`: `ToXml`, `ToBytes`, `Write`, `FileNameFor` and `CreateAttachment`, which returns a `PdfAttachment` for PDF/A-3b with `Relationship = Alternative`) writes an `IsdocInvoice` (supplier and customer `IsdocParty`, `IsdocInvoiceLine` with prices without VAT and the VAT rate, `IsdocPayment`, all document types of ISDOC, a non-payer, a credit note with negative quantities) as ISDOC. The totals, the VAT recapitulation by rate and the amount to pay are calculated bottom-up by `IsdocInvoice.CalculateTotals()` (the explicit `BaseAmount`, `VatAmount` and `VatRecap` of a printed invoice take precedence, `PaidDeposits` and `RoundPayable` are supported); the UUID defaults to a stable one derived from the supplier company id and the number (a repeated import does not duplicate the document); the account number and the bank code come from a Czech IBAN, the house number from the end of the street. An invalid document is an `ArgumentException`. The output is validated against the official `isdoc-invoice-6.0.2.xsd` in the tests. Purely additive, the renderer does not use it.
- Fix: HTML output: the base CSS (and the print page CSS) was written to `<style>` as escaped text, so the child selector `.nr-keep-together>*` was written as `.nr-keep-together>*` and did not apply in the browser. The CSS is now written unescaped (CDATA that is removed after serialization, as the debug CSS already was).
- Templates: the new content nodes `Emoji` and `Icon`, drawn as **vectors** so that the result is the same in PDF, SVG, WebP and HTML and does not depend on the fonts of the machine (a Docker image has only Lato; text with `fontFallbacks` renders an emoji as an empty space there). `Emoji` (`emoji`, `sizeMm`) draws a color emoji from the bundled Twemoji 15.0 set (3720 emoji, found by code points; a variation selector does not matter; skin tones, flags and ZWJ sequences work). `Icon` (`name`, `variant`, `version`, `sizeMm`) draws a Font Awesome icon filled with `style.color` (black without it; a `#AARRGGBB` color keeps its transparency as `fill-opacity`): `house`, `fa-house`, an alias such as `home` or copied classes such as `fa-solid fa-house` all work, the variant defaults to `solid`, `regular` or `brands` by what the icon has, `light`, `thin`, `duotone` and `sharp-*` come with Font Awesome Pro, `version` picks `7`, `6` or an exact version. The size defaults to 1.2 times the font size (as for `Checkbox`), an icon keeps its aspect ratio. Font Awesome Free 7.3.1 (1992 icons) is bundled, about 2 MB of the package together with Twemoji; licenses (CC BY 4.0, SIL OFL, MIT) are in the new `THIRD-PARTY-NOTICES.md` of the package. Diagnostics: `NRP039` an emoji is not in the set (a static value is checked by the validation, a value from the data at render time), `NRP040` an icon is in no available pack (a typo, or a Pro icon without the Pro pack), `NRP041` the requested variant or version is not available and the nearest one was used (`light` becomes `regular`, `duotone` becomes `solid`); an unknown emoji or icon draws an empty space of the node size so that the layout does not change. Icons are not checked statically because a Pro icon is known only to the host.
- Library: `RenderOptions.Icons` (`IIconCatalog`, default `IconCatalog.Default` with Font Awesome Free and Twemoji) and the new `NetReporter.Icons` namespace: `IconCatalog` (`FindIcon`, `TryGetEmojiSvg`, `Create(cacheDirectory)` and `Reload()` for packs downloaded to a cache directory), `IconPack`, `EmojiPack`, `IconPackBuilder` and `FontAwesomeInstaller`. The installer downloads Font Awesome from npm into the cache: Free from `registry.npmjs.org`, **Pro with the user's license key from `npm.fontawesome.com`**, by an exact version or by a major version (`7` is the highest stable 7.x; Font Awesome 6 and 7 are supported). The key goes only into the `Authorization` header of `npm.fontawesome.com`, never into a URL, a log, an exception message or a file, and a template cannot set it. Only https and the two npm hosts are allowed, the URL of the package must be on the same host, redirects are followed by hand and only to an allowed host (the Pro registry answers a tarball request with a `307` to a pre-signed `dl.fontawesome.com` URL: that one host is allowed as a redirect target only and the key is never sent to it; error messages never contain a path or query because the signature is in it), the download has a size (200 MB) and time (5 minutes) limit, only the metadata are read from the package and the result is written atomically. The metadata of Font Awesome Pro 7 are 115 MB, so `icon-families.json` is read as a stream icon by icon (`Utf8JsonReader`), not as one document; a Pro 7.3.1 pack (4349 icons in 17 variants) has 6.5 MB on disk and about 100 MB in memory after loading. The secondary layer of a duotone icon is recognized by the `fa-secondary` class (Font Awesome 6) and by `opacity=".4"` on the path (Font Awesome 7 Pro). Pro files must not be redistributed, so the cache stays outside the repository and the Docker images. `IRenderSink` has the new default method `Symbol(style, ResolvedSymbol, elementId)` (the default draws the SVG as an `Image`), so custom sinks keep working; `HtmlRenderSink` and `JsonRenderSink` override it. The packs are generated by `tools/IconPacks`. Docs: `02_template_format.md` sections 7.13 and 7.14 and the new chapter `18-emoji-a-ikony.md`.
- Template editor: **emoji and icons**. The tree toolbar has the buttons Emoji and Icon and the inspector of an `Emoji` or `Icon` node has **Změnit emoji… / Změnit ikonu…**; they open a picker (`SymbolPickerDialog`): emoji by group, with skin tones and a search in Czech and English without diacritics (Czech names and keywords from Unicode CLDR 48, order and groups from emojibase-data 17, `Data/emoji-picker.json`, generated by `tools/IconPacks emoji-picker`), Font Awesome icons by name, alias, label and search terms with a filter of the variant and of the version. A double click or **Vložit** inserts the node as one undo step, **Použít** changes the selected node. The variant is written to the template only when it differs from the automatic one, so the JSON stays short. Previews come from `GET symbols/emoji/{key}.svg` and `GET symbols/icon/{variant}/{name}.svg` (one SVG each, strict input checks, `nosniff` and a CSP; Pro previews are `Cache-Control: private`, and the editor has no sign-in yet, so anyone who reaches it sees them). The **FontAwesome** button of the picker shows the built-in Free version, the downloaded packs and whether the license key is set (never its value), and has the buttons **Stáhnout Pro 7**, **Stáhnout Pro 6** (disabled without a key) and **Stáhnout Free 6**; the key is never typed into the editor: it is read from the environment variable `FONTAWESOME_PRO_TOKEN` (or `FONTAWESOME_NPM_AUTH_TOKEN`, or `FA_TOKEN`), `NETREPORTER_ICONS_DIR` sets the cache directory (default `/tmp/netreporter-icons` in a container) and `FONTAWESOME_VERSIONS=7` installs the missing Pro versions at start. The preview and the gallery thumbnails use the same catalog, so Pro icons render there too. Sample: `ikony-a-emoji` (with sample data). Deployment: the optional key `FONTAWESOME_PRO_TOKEN` of the secret `netreporter-secrets` (the 5th column `optional` of `04-secret-map.txt`: `deploy.sh` skips an empty value instead of failing).
- Template editor and ReportServer UI: new look. Compact, industrial design (13 px base font, dense tables and property grids, 4 px grid, indigo accent, Inter) in the spirit of the Syncfusion Essential UI Kit, with a light and a dark mode (the choice is remembered; Monaco switches too). Template editor: a new shell with an app bar (**Načíst šablonu**, **Uložit šablonu**, **Reset** and the theme switch moved there from the tabs), the structure tree in a side panel, the tabs fill the window height (no more `calc(100vh - ...)`), the Page tab is a set of cards, and the Syncfusion theme is `tailwind3` (was `bootstrap5`). ReportServer UI: a sidebar with the sections, a header with the API / DB / S3 status badges and a split sign-in page; the pages use status badges, alerts and dense tables instead of inline styles and emoji. Both applications are meant for a desktop screen in landscape. The shared stylesheet `wwwroot/css/nr-ui.css` and `wwwroot/js/nr-theme.js` exist in two identical copies (a test checks they match).
- New package **`NETReporter.Abstractions`**: the shared contract of the library and of the new client. It holds the interfaces `IReportRenderer` (all public overloads of `RenderAsync` and `Render*PagesAsync`, implemented by `ReportRenderer`) and `ITemplateTools` (`ValidateTemplateAsync`, `ValidateDataAsync`, `GetDataContractAsync`, `GenerateSampleDataAsync`; the new class `TemplateTools` implements it locally: validation by the parser, data validation by a render to JSON that never calls the application's observer and never touches the disk, data contract and sample data) with the result types `TemplateValidationResult`, `TemplateDataValidationResult` and `TemplateSampleDataResult`, and the types the library moved there without changing their namespaces: `RenderOptions` (and `RenderFormat`, `ImageQuality`, `BindingDiagnosticsCallback`), `PdfConformance`, `PdfAttachment`, `PdfEncryption`, `RenderPageLimitExceededException`, `RenderOptionsValidator` (now public), `Diagnostic`, `DiagnosticSeverity`, `DiagnosticCodes`, `BindingDiagnostic`, `BindingDiagSeverity`, `IReportPipelineObserver` with its contexts, `ParserValidationMode`, the data contract types (`TemplateDataContract`, `DataField`, `SampleDataOptions`…) and `IIconCatalog` with the icon data records. `NETReporter` references the package (same version) and forwards every moved type (`TypeForwardedTo`), so source and binary compatibility is kept; the only visible change is that `IconGlyph.ToSvg(color)` is now an extension method of `NetReporter.Icons.IconGlyphExtensions` (calls look the same). `AddNetReporter()` also registers `IReportRenderer` and `ITemplateTools` (`TryAdd`, so an earlier registration, for example the client, wins). The Abstractions package has no dependency on QuestPDF, Roslyn, OxyPlot or ZXing.
- New option `RenderOptions.AllowLocalImageSources` (default `true`): with `false` the renderer never reads an image or an SVG from a file (`Image.src`, watermark image, `{{$.Assets.*}}` with a path), draws the placeholder and, in the JSON output, reports the source as `unavailable` with the reason `local-sources-disabled` whether the file exists or not. Data URIs and http(s) addresses are not affected. It joins `AllowLocalPdfSources` for servers that render foreign templates.
- New package **`NETReporter.Client`**: a thin client of ReportServer with the same interfaces. `ReportServerRenderer` (`IReportRenderer`) renders on the server through `POST render/ad-hoc` (and `render/ad-hoc/pages`) and `ReportServerTemplateTools` (`ITemplateTools`) calls `/template-tools`; both authenticate with an **API token** (`Authorization: Bearer nrp_…`). All formats and all `RenderOptions` work (PDF/A, attachments, encryption, watermark, resolution…), `OnBindingDiagnostics` and the pipeline observer are supported (the diagnostics are read from the render job; the observer gets simplified events, the parse stage happens on the server). Registration: `services.AddNetReporterClient(options => …)` or `AddNetReporterClient(IConfiguration)` (section `NetReporterClient`: `BaseAddress`, `ApiToken`, `Timeout`) replaces the local `IReportRenderer` and `ITemplateTools` whatever the order of `AddNetReporter()`, validates the settings at start and returns the `IHttpClientBuilder`; without DI use `NetReporterClient.CreateHttpClient(options)`. Invalid options are rejected locally with `ArgumentException` before anything is sent; server errors map to the exceptions of the local renderer (`RenderPageLimitExceededException`, `OperationCanceledException`, `ArgumentException` for options) and to the new `ReportServerException : InvalidOperationException` (`Code`, `StatusCode`, `Diagnostics`, `RenderJobId`; for example `TEMPLATE_VALIDATION_FAILED`, `AUTH_UNAUTHORIZED`, `REPORTSERVER_UNAVAILABLE`, `REPORTSERVER_TIMEOUT`). The remote render differs from the local one in what the server decides: files are never read, the server caps pages, resolution and attachments, `RenderOptions.Icons` is not used and at most 50 diagnostics are returned. Docs: the new chapter `client-usage` (`doc/netreporter-knihovna/user/pouziti-klienta.md`, also in the package).
- Packaging: the three packages share the metadata, the release notes taken from this `CHANGELOG.md` and the version (`src/Packaging.props`, MinVer); publish them together, the order is `NETReporter.Abstractions`, `NETReporter`, `NETReporter.Client` (`nugetpub.ps1` does it).
- ReportServer API: **render options and all formats**. `POST /render/ad-hoc` takes the optional `options` (`RenderOptionsRequest`: `format` pdf, html, json, svg, webp, png or jpeg, `renderBindingsAsExpressions`, `debugMode`, `maxPages`, `maxBindingDiagnostics`, `validationMode`, `watermark`, `pdfConformance`, `attachments` as base64, `embedData`, `encryption`, `imageDpi`, `imageQuality`) and returns the output in the right content type; the new `POST /render/ad-hoc/pages` returns every page of an SVG, WebP, PNG or JPEG render as JSON (SVG as text, images as base64) with the binding diagnostics inline. The options are validated before a job is created (`400 RENDER_INVALID_OPTIONS`; attachments over the limit `413`); a page limit overrun is `400 RENDER_PAGE_LIMIT_EXCEEDED` with `maxPages`. Only a PDF without encryption is cached and stored (the cache key includes the options that change the output; without options it is the same as before), the other outputs answer `X-Render-Cache: bypass`. New response headers `X-Render-Format`, `X-Render-Page-Count`, `X-Render-Binding-Diagnostics` (the count; the details are in the job detail) and `X-Render-Binding-Diagnostics-Omitted`. `RenderJob` has the new column `Format` (migration `AddRenderJobFormat`, default `pdf`), returned by the job detail and list. New limits `SECURITY_REQUESTLIMITS_MAX_RENDER_PAGES` (10000), `…_MAX_IMAGE_RENDER_PAGES` (100), `…_MAX_IMAGE_DPI` (300) and `…_MAX_RENDER_ATTACHMENT_BYTES` (5 MiB).
- ReportServer API: **template tools** `POST /template-tools/validate`, `/validate-data`, `/data-contract` and `/sample-data` (User policy, render limiter, payload limit): they only call `ITemplateTools`, so the result equals the local one; nothing is stored.
- ReportServer API and UI: **API tokens**. A user creates tokens on the new UI page **API tokeny** (name and expiry of 30, 90 or 365 days, none or a custom date); the plain text is shown once and only its SHA-256 hash is stored (`ApiTokens`, migration `AddApiTokens`, the prefix `nrp_` and 8 characters identify it in lists). A token has the rights of its owner (no scopes), is checked on every request without caching (a revoked or expired token and a deactivated owner stop working at once) and sees only the owner's templates and jobs. `GET/POST /auth/tokens`, `DELETE /auth/tokens/{id}` and, for administrators, `GET /admin/api-tokens?userId=` and `DELETE /admin/api-tokens/{id}`; at most 20 active tokens per user; the page management works only with a JWT (a token cannot create or revoke tokens, `403`). Deleting a user deletes the tokens, the audit log (category `ApiToken`) never contains the token. The users page links to the tokens of each user; the jobs table shows the output format.
- **Behavior changes** of ReportServer API: (1) the server never reads files for a render: `AllowLocalPdfSources` and `AllowLocalImageSources` are forced to `false` for ad-hoc and stored templates alike, so a letterhead or an image given as a file path now renders without it (the warning `NRP036` for PDF sources, the placeholder for images) and only data URIs and http(s) addresses work; (2) the render rate limit is counted per user (authenticated principal, fallback to the client address) instead of one limit for everybody, and the rate limiter runs after authentication; (3) the authentication scheme of the API is a policy scheme that sends `Authorization: Bearer nrp_…` to the API token handler and everything else to JWT (the 401 and 403 bodies are unchanged); (4) a render cancelled by the client is recorded as `Failed` with `RENDER_CANCELLED` instead of staying in `Running` (ToDo #64).
- ReportServer UI: error messages of the API are shown as text (the camelCase body of the API was not read, so the raw JSON appeared).
Celý seznam změn: CHANGELOG.md v balíčku a https://dev.azure.com/advsoft/Knihovny/_git/NetReporter?path=/src/NetReporter/CHANGELOG.md