# XML Receipt Template Syntax ## Overview Templates are XML documents that combine static text, data binding expressions, and control flow to produce a list of `PrintCommand` objects for Epson thermal receipt printers. ```csharp var engine = new ReceiptTemplateEngine(); List commands = engine.Render( xmlTemplate, // XML template string jsonData, // JSON data string lineWidth: 42, // characters per line (normal font) bigFontLineWidth: 22 // characters per line (big font) ); ``` --- ## Root Element ### `` Required root element. All other elements must be nested inside it. ```xml ``` --- ## Content Elements ### `` Outputs a single line of text. The most common element. **Attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `align` | `left` / `center` / `right` | `left` | Text alignment within the line width | | `bold` | `true` / `false` | `false` | Bold text | | `big` | `true` / `false` | `false` | Double-width font (uses `bigFontLineWidth` for alignment) | | `tall` | `true` / `false` | `false` | Double-height font | | `red` | `true` / `false` | `false` | Red text (on supported printers) | | `lineSpacing` | int | _(none)_ | Override line spacing in dots | | `wrap` | `true` / `false` | `false` | Word-wrap text that exceeds line width | | `wrapIndent` | int | `0` | Indent (in characters) for continuation lines when wrapping | **Examples:** ```xml Hello World RECEIPT {{Title}} --------- {{dish.Number}}x {{dish.Name}} 1x Very Long Dish Name That Will Wrap To Next Line Spaced out text ``` When `big="true"`, alignment uses `bigFontLineWidth` instead of `lineWidth`. --- ### `` Two-column layout: left-aligned left text, right-aligned right text. The line width is split in half. **Attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `left` | string | `""` | Left column content (supports `{{}}` expressions) | | `right` | string | `""` | Right column content (supports `{{}}` expressions) | | `bold` | `true` / `false` | `false` | Bold text | | `big` | `true` / `false` | `false` | Double-width font | | `tall` | `true` / `false` | `false` | Double-height font | | `red` | `true` / `false` | `false` | Red text | | `lineSpacing` | int | _(none)_ | Override line spacing | | `wrap` | `true` / `false` | `false` | Wrap left column if combined text exceeds line width | | `wrapIndent` | int | `0` | Indent for wrapped continuation lines | **Examples:** ```xml ``` Without `wrap`, the left column is padded to half the line width and the right column is right-padded to fill the remaining space. With `wrap="true"`, if the combined text exceeds the line width, the left column wraps and the right column appears right-aligned on the first line. --- ### `` Multi-column layout with explicit column definitions. Each column is defined by a nested `` element. **Row attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `bold` | `true` / `false` | `false` | Bold text | | `big` | `true` / `false` | `false` | Double-width font | | `tall` | `true` / `false` | `false` | Double-height font | | `red` | `true` / `false` | `false` | Red text | | `lineSpacing` | int | _(none)_ | Override line spacing | **`` attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `width` | int | _(auto)_ | Column width in characters. Unspecified columns share remaining space equally. | | `align` | `left` / `center` / `right` | `left` | Text alignment within the column | **Examples:** ```xml MwSt % Brutto Netto MwSt {{tax.Category}}:{{tax.Rate}}% {{tax.Gross:F2}} {{tax.Currency}} {{tax.Net:F2}} {{tax.Currency}} {{tax.TaxAmount:F2}} {{tax.Currency}} ``` In this example, the first three columns are 10 characters wide. The fourth column gets all remaining space (`lineWidth - 30`). --- ### `` Outputs a line of repeated characters spanning the full line width. **Attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `char` | single char | `-` | Character to repeat | **Examples:** ```xml ``` Always uses `lineWidth` (not `bigFontLineWidth`). --- ### `` Triggers a paper cut. Produces a `PrintCommand` with `IsCut = true`. ```xml ``` --- ### `` Outputs one or more empty lines. **Attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `lines` | int | `1` | Number of empty lines | **Examples:** ```xml ``` --- ### `` Renders an ASCII box table with borders (`+`, `-`, `|`). Column definitions are provided via nested `` elements. **Attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `items` | string | _(none)_ | JSON array path for data rows | | `var` | string | `"item"` | Loop variable name for each data row | | `headerItems` | string | _(none)_ | JSON array path for header rows (optional) | If `headerItems` is not set, the text content of `` elements is used as the header row. Columns without text produce no header. **`` attributes:** | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `width` | int | _(auto)_ | Column width in characters (excluding border characters) | | `align` | `left` / `center` / `right` | `left` | Cell content alignment | Column widths exclude border characters. With 3 columns, 4 border characters (`|`) are used, so the available content width is `lineWidth - 4`. **Examples:** ```xml
Order:QR InfoDate
NamePrice
``` --- ## Control Flow ### `` Iterates over a JSON array. For each item, the child elements are rendered with the loop variable available in the data context. **Attributes (both required):** | Attribute | Type | Description | |-----------|------|-------------| | `items` | string | Path to the JSON array (supports dot notation for nested arrays) | | `var` | string | Variable name to bind each array element to | **Loop variables** (available inside ``): | Variable | Type | Description | |----------|------|-------------| | `{{$index}}` | int | Zero-based index of the current item | | `{{$first}}` | bool | `true` for the first item | | `{{$last}}` | bool | `true` for the last item | **Examples:** ```xml {{item.Name}}: {{item.Price:F2}} {{gang.Id}}. {{gang.Name}} {{dish.Number}}x {{dish.Name}} {{$index}}: {{item.Name}} {{gang.Name}} ``` If `items` resolves to `null`, an empty array, or a non-array value, the loop body is skipped entirely. --- ### `` / `` Conditionally renders child elements. An `` block is optional and must immediately follow its corresponding ``. **Attributes:** | Attribute | Type | Description | |-----------|------|-------------| | `test` | string | Condition expression (see Condition Expressions below) | **Examples:** ```xml {{SpecialInstruction}} Discount: {{DiscountInfo.Description}} No discount applied Large order! Active order Modified First item header ``` --- ## Data Binding ### Expression Syntax `{{}}` Expressions are enclosed in double curly braces and can appear in text content, `left`/`right` attributes of ``, and `` text content. **Property access:** ```xml {{Name}} {{Person.Address.City}} {{FirstName}} {{LastName}} Order #{{OrderNumber}} - Table {{TableNumber}} {{item.Name}} - {{item.Price}} ``` Property lookup is **case-insensitive**. Both `{{name}}` and `{{Name}}` resolve the same JSON property. If a property is missing or `null`, the expression resolves to an empty string. ### Format Strings Use a colon after the property name to apply a format string: ``` {{PropertyName:format}} ``` **DateTime formatting** (the JSON value must be an ISO 8601 date string): ```xml {{DateTime:dd.MM.yyyy}} {{DateTime:HH:mm:ss}} {{DateTime:dd-MMM-yy HH:mm}} ``` **Number formatting** (standard .NET format strings): ```xml {{Price:F2}} {{Amount:N0}} ``` ### Loop Variables Available only inside `` blocks: ```xml {{$index}}: {{item.Name}} ``` | Variable | Type | Description | |----------|------|-------------| | `{{$index}}` | int | Zero-based index | | `{{$first}}` | bool | `true` on first iteration | | `{{$last}}` | bool | `true` on last iteration | `$first` and `$last` are primarily useful in `` conditions rather than text output. --- ## Condition Expressions Used in the `test` attribute of ``. The following forms are supported: ### Truthiness A property path by itself checks whether the value is "truthy": ```xml ``` | JSON value | Truthy? | |------------|---------| | `"hello"` | yes | | `""` | no | | `42` | yes | | `0` | no | | `true` | yes | | `false` | no | | `[1, 2]` | yes | | `[]` | no | | `null` | no | | _(missing)_ | no | | `{ ... }` | yes | ### Negation Prefix with `!` to negate: ```xml ``` ### Comparisons Six comparison operators are supported. Operands can be property paths, numeric literals, or quoted string literals: ```xml ``` If both operands parse as numbers, numeric comparison is used. Otherwise, case-insensitive string comparison is used. Comparisons can also be negated: ```xml ``` ### Loop Variables in Conditions ```xml ``` --- ## Complete Example ### Kitchen Receipt Template ```xml {{Title}} {{TransactionDateTime:dd-MMM-yy HH:mm}} Nr.:{{ReceiptNumber}} {{WaiterName}} Tisch: {{TableNumber}} {{SpecialInstruction}} {{gang.Id}}. {{gang.Name}} {{dish.Number}}x {{dish.Name}} Comment: {{dish.Comment}} ``` ### Sample JSON Data ```json { "Title": "Ristorante Bella", "TransactionDateTime": "2024-03-15T14:30:00Z", "ReceiptNumber": "42", "WaiterName": "Max Mustermann", "TableNumber": "5", "SpecialInstruction": null, "Gangs": [ { "Id": 1, "Name": "Vorspeise", "Dishes": [ { "Number": 2, "Name": "Caesar Salad", "Comment": null } ] }, { "Id": 2, "Name": "Hauptgang", "Dishes": [ { "Number": 1, "Name": "Wiener Schnitzel", "Comment": "Well done" } ] } ] } ``` ### Printed Output (42 char width) ``` Ristorante Bella ------------------------------------------ 15-Mar-24 14:30 Nr.:42 Max Mustermann Tisch: 5 ------------------------------------------ 1. Vorspeise 2x Caesar Salad ------------------------------------------ 2. Hauptgang 1x Wiener Schnitzel Comment: Well done ------------------------------------------ ``` --- ## Quick Reference | Element | Produces | Key Attributes | |---------|----------|----------------| | `` | _(root)_ | - | | `` | 1 PrintCommand (or N if wrapping) | `align`, `bold`, `big`, `tall`, `red`, `wrap`, `wrapIndent`, `lineSpacing` | | `` | 1 PrintCommand (or N if wrapping) | `left`, `right`, `bold`, `big`, `tall`, `red`, `wrap`, `wrapIndent`, `lineSpacing` | | `` | 1 PrintCommand | `bold`, `big`, `tall`, `red`, `lineSpacing` + nested `` | | `` | 1 PrintCommand | `char` | | `` | 1 PrintCommand (IsCut=true) | - | | `` | N PrintCommands (empty lines) | `lines` | | `` | N x children | `items` (required), `var` (required) | | `` | 0 or children | `test` (required) | | `` | 0 or children | _(must follow ``)_ | | `` | Multiple PrintCommands (bordered) | `items`, `var`, `headerItems` + nested `` |