Files
Print_server/template_syntax.md
2026-01-22 11:13:07 +01:00

947 lines
15 KiB
Markdown

# Receipt Template Language (RTL) Syntax Documentation
This document describes the syntax for creating receipt templates used by the Epson thermal printer template engine.
## Table of Contents
1. [Overview](#overview)
2. [Basic Text](#basic-text)
3. [Data Bindings](#data-bindings)
4. [Styled Text](#styled-text)
5. [Separators](#separators)
6. [Comments](#comments)
7. [Conditionals](#conditionals)
8. [Loops](#loops)
9. [Rows and Columns](#rows-and-columns)
10. [Complete Example](#complete-example)
11. [Printer Profiles](#printer-profiles)
---
## Overview
RTL is a line-based template language designed for thermal receipt printers. Templates are plain text files with embedded directives, bindings, and style markers that get transformed into printer commands.
**File Extension:** `.template`
**Key Concepts:**
- Templates are processed line by line
- Data is provided as JSON and accessed via bindings `{path}`
- Styles are applied using `#style# text #` syntax
- Control flow uses `@directive` syntax
---
## Basic Text
Plain text is output directly to the printer.
```
Hello World
This is plain text
```
**Output:**
```
Hello World
This is plain text
```
### Empty Lines
Empty lines in templates produce empty lines on the receipt.
```
Line 1
Line 3
```
---
## Data Bindings
Bindings insert values from the JSON data into the output.
### Simple Binding
```
{PropertyName}
```
**JSON:**
```json
{"PropertyName": "Hello"}
```
**Output:**
```
Hello
```
### Nested Properties
Use dot notation to access nested objects.
```
{Customer.Name}
{Order.Items.0.Name}
```
**JSON:**
```json
{
"Customer": {"Name": "John Doe"},
"Order": {"Items": [{"Name": "Coffee"}]}
}
```
**Output:**
```
John Doe
Coffee
```
### Format Specifiers
Add a format string after a colon to format numbers and dates.
#### Numeric Formats
```
Total: {Amount:F2}
Order #: {OrderNumber:D4}
```
**JSON:**
```json
{"Amount": 19.5, "OrderNumber": 42}
```
**Output:**
```
Total: 19.50
Order #: 0042
```
| Format | Description | Example Input | Output |
|--------|-------------|---------------|--------|
| `F2` | Fixed-point, 2 decimals | 19.5 | 19.50 |
| `F0` | Fixed-point, no decimals | 19.5 | 20 |
| `D4` | Decimal, padded to 4 digits | 42 | 0042 |
| `N2` | Number with grouping | 1234.5 | 1,234.50 |
#### Date/Time Formats
```
Date: {TransactionDateTime:dd-MMM-yy}
Time: {TransactionDateTime:HH:mm}
Full: {TransactionDateTime:yyyy-MM-dd HH:mm:ss}
```
**JSON:**
```json
{"TransactionDateTime": "2024-03-15T14:30:00"}
```
**Output:**
```
Date: 15-Mar-24
Time: 14:30
Full: 2024-03-15 14:30:00
```
| Format | Description | Example Output |
|--------|-------------|----------------|
| `dd-MMM-yy` | Day-Month-Year | 15-Mar-24 |
| `dd/MM/yyyy` | European date | 15/03/2024 |
| `MM/dd/yyyy` | US date | 03/15/2024 |
| `HH:mm` | 24-hour time | 14:30 |
| `hh:mm tt` | 12-hour time | 02:30 PM |
### Array Count
Access the count/length of an array.
```
Items: {Items.count}
```
**JSON:**
```json
{"Items": ["A", "B", "C"]}
```
**Output:**
```
Items: 3
```
### Missing Values
If a binding path doesn't exist or is null, an empty string is output.
```
Name: {MissingProperty}
```
**Output:**
```
Name:
```
---
## Styled Text
Apply formatting styles to text using the `#styles# content #` syntax.
### Basic Syntax
```
#style1,style2# text content #
```
The styles are comma-separated and applied to all content between the markers.
### Available Styles
| Style | Description | PrintCommand Property |
|-------|-------------|----------------------|
| `bold` | Bold text | `IsBold = true` |
| `big` | Double-width and double-height | `IsBig = true` |
| `tall` | Double-height only | `IsTall = true` |
| `red` | Red color (if printer supports) | `IsRed = true` |
| `center` | Center-align text | Text padded with spaces |
| `right` | Right-align text | Text padded with spaces |
| `left` | Left-align text (default) | No padding |
| `spacing:N` | Set line spacing to N | `SetLineSpacing = N` |
### Examples
#### Bold Text
```
#bold# Important Notice #
```
#### Centered Title
```
#center# RECEIPT #
```
#### Combined Styles
```
#bold,big,center# RESTAURANT NAME #
```
#### Red Text (Impact Printers)
```
#red,bold# WARNING #
```
#### With Bindings
```
#bold,center# {Title} #
#big# Table: {TableNumber} #
```
#### Line Spacing
```
#spacing:50# Spaced text #
```
### Style Scope
Styles apply only to content within the markers on the same line.
```
#bold# This is bold # but this is not
```
---
## Separators
Create horizontal lines using repeated characters.
### Dash Separator
```
---
```
Output: `------------------------------------------------` (full line width)
### Equals Separator
```
===
```
Output: `================================================`
### Star Separator
```
***
```
Output: `************************************************`
### Tilde Separator
```
~~~
```
Output: `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
**Note:** Separator width automatically matches the printer's line width (48 chars for TM-T30III, 33 chars for TM-U220II).
---
## Comments
Add comments to templates for documentation purposes. Comments are stripped during parsing and do not appear in the output.
### Single-Line Comment
Use `@*` at the start of a line to comment out the entire line.
```
@* This is a single-line comment
Regular text here
```
**Output:**
```
Regular text here
```
### Inline Comment
Use `@* ... *@` to create a comment that can have content after it on the same line.
```
@* This is a comment *@
@* Header section *@
#bold,center# Title #
```
**Output:**
```
Title
```
### Multi-Line Comment
Use `@*` on its own line to start a multi-line comment block, and `*@` to close it.
```
@*
This is a multi-line comment.
All these lines are ignored.
Directives like @if are also ignored here.
*@
This text appears in output
```
**Output:**
```
This text appears in output
```
### Use Cases
#### Document Template Sections
```
@* === HEADER SECTION === *@
#bold,center# {Title} #
---
@* === ITEMS SECTION === *@
@foreach item in Items
{item.Name}
@end
```
#### Temporarily Disable Code
```
@*
@if DebugMode
Debug info: {DebugData}
@end
*@
```
#### Add Notes for Maintainers
```
@* Note: This section only shows for orders over $100 *@
@if Total > 100
#bold# Large Order #
@end
```
### Important Notes
- Comments must start at the beginning of a line (after optional whitespace)
- `@*` mid-line in content is treated as regular text, not a comment
- Star separators (`***`) are not confused with comments
- Unclosed multi-line comments produce a lexer error
---
## Conditionals
Control which content is rendered based on data values.
### Basic If
```
@if Condition
Content shown when true
@end
```
### If-Else
```
@if HasDiscount
Discount Applied!
@else
No Discount
@end
```
### If-ElseIf-Else
```
@if Status == "pending"
Order Pending
@elseif Status == "complete"
Order Complete
@else
Unknown Status
@end
```
### Truthy/Falsy Values
The following are considered **falsy**:
- `null` or missing property
- `false`
- Empty string `""`
- Number `0`
- Empty array `[]`
Everything else is **truthy**.
### Comparison Operators
| Operator | Description | Example |
|----------|-------------|---------|
| `==` | Equal | `Status == "active"` |
| `!=` | Not equal | `Type != "void"` |
| `>` | Greater than | `Amount > 100` |
| `<` | Less than | `Count < 5` |
| `>=` | Greater or equal | `Total >= 50` |
| `<=` | Less or equal | `Qty <= 10` |
### Negation
```
@if !IsVoided
Valid Order
@end
```
### String Literals
Use quotes for string comparisons.
```
@if Status == "active"
Active
@end
```
### Numeric Comparisons
```
@if Amount > 100
Large Order
@end
@if Items.count > 0
Has Items
@end
```
### Examples
#### Check for Optional Field
```
@if SpecialInstruction
#bold# Note: {SpecialInstruction} #
@end
```
#### Check Array Has Items
```
@if Modifications.count > 0
Modifications:
@foreach mod in Modifications
- {mod}
@end
@end
```
#### Conditional Separator
```
@if Items.count > 0
---
@end
```
---
## Loops
Iterate over arrays in the data.
### Basic Foreach
```
@foreach item in Items
{item}
@end
```
**JSON:**
```json
{"Items": ["Apple", "Banana", "Cherry"]}
```
**Output:**
```
Apple
Banana
Cherry
```
### Object Properties
```
@foreach product in Products
{product.Name} - {product.Price:F2}
@end
```
**JSON:**
```json
{
"Products": [
{"Name": "Coffee", "Price": 3.50},
{"Name": "Tea", "Price": 2.50}
]
}
```
**Output:**
```
Coffee - 3.50
Tea - 2.50
```
### Nested Loops
```
@foreach category in Categories
#bold# {category.Name} #
@foreach item in category.Items
{item.Name}
@end
@end
```
### Loop Metadata Variables
Inside a loop, these special variables are available:
| Variable | Description | Example Value |
|----------|-------------|---------------|
| `_index` | Zero-based index | 0, 1, 2, ... |
| `_number` | One-based number | 1, 2, 3, ... |
| `_first` | True if first item | true/false |
| `_last` | True if last item | true/false |
| `_count` | Total items in collection | 5 |
#### Examples
```
@foreach item in Items
{_number}. {item.Name}
@end
```
**Output:**
```
1. Apple
2. Banana
3. Cherry
```
```
@foreach item in Items
{item}
@if !_last
---
@end
@end
```
**Output:**
```
Apple
---
Banana
---
Cherry
```
### Nested Path Collections
```
@foreach dish in Order.Kitchen.Dishes
{dish.Name}
@end
```
---
## Rows and Columns
Create tabular layouts with fixed-width columns.
### Basic Row
```
@row
|width|content|width|content
@endrow
```
### Row Styles
Apply styles to an entire row by adding style names after `@row`:
```
@row bold
|width|content|width|content
@endrow
```
Multiple styles can be combined with commas:
```
@row bold, big
|width|content|width|content
@endrow
```
#### Available Row Styles
| Style | Description |
|-------|-------------|
| `bold` | Bold text for entire row |
| `big` | Double-width and double-height |
| `tall` | Double-height only |
| `red` | Red color (if printer supports) |
| `spacing:N` | Set line spacing to N |
#### Row Style Examples
```
@row bold
|30|{Name}|10,right|{Price:F2}
@endrow
```
```
@row bold, big
|15|TOTAL|9,right|{Total:F2}
@endrow
```
```
@row red, bold
|40|*** WARNING ***
@endrow
```
### Column Syntax
```
|width|content
|width,alignment|content
```
- **width**: Number of characters for the column
- **alignment**: `left` (default), `right`, or `center`
### Examples
#### Two Columns
```
@row
|30|{Name}|10,right|{Price:F2}
@endrow
```
**JSON:**
```json
{"Name": "Coffee", "Price": 3.50}
```
**Output:**
```
Coffee 3.50
```
#### Three Columns
```
@row
|5,right|{Qty}|25|{Name}|10,right|{Total:F2}
@endrow
```
**Output:**
```
2 Espresso 7.00
```
#### Header Row
```
@row
|5|Qty|25|Item|10,right|Price
@endrow
===
```
### Columns in Loops
```
@foreach item in Items
@row
|5,right|{item.Quantity}|25|{item.Name}|10,right|{item.Price:F2}
@endrow
@end
```
### Text Truncation
If content exceeds the column width, it is truncated.
```
@row
|10|VeryLongProductNameHere
@endrow
```
**Output:**
```
VeryLongPr
```
---
## Complete Example
Here's a complete kitchen receipt template:
```
#red,big,tall,center# {Title} #
---
#center# {TransactionDateTime:dd-MMM-yy HH:mm} Nr.:{ReceiptNumber} #
#center# {WaiterName} #
#center# {WaiterId} #
#big,bold,center# Tisch: {TableNumber} #
@if SpecialInstruction
#big,bold,center# {SpecialInstruction} #
@end
---
@foreach gang in Gangs
#red,big,tall,center# {gang.Id}. {gang.Name} #
@foreach dish in gang.Dishes
#tall# {dish.Number}x {dish.Name} #
@if dish.Modifications.Removed.count > 0
@foreach removed in dish.Modifications.Removed
#bold,tall# - {removed} #
@end
@end
@if dish.Modifications.Added.count > 0
@foreach added in dish.Modifications.Added
#bold,tall# + {added} #
@end
@end
@if dish.Comment
#bold,tall# Comment: {dish.Comment} #
@end
@end
@end
@if Gangs.count > 0
---
@end
@foreach dish in Dishes
#tall# {dish.Number}x {dish.Name} #
@if dish.Modifications.Removed.count > 0
@foreach removed in dish.Modifications.Removed
#bold,tall# - {removed} #
@end
@end
@if dish.Modifications.Added.count > 0
@foreach added in dish.Modifications.Added
#bold,tall# + {added} #
@end
@end
@if dish.Comment
#bold,tall# Comment: {dish.Comment} #
@end
@end
@if Dishes.count > 0
---
@end
```
**Sample JSON:**
```json
{
"Title": "Restaurant Kitchen",
"TransactionDateTime": "2024-03-15T14:30:00",
"ReceiptNumber": "12345",
"WaiterName": "John Doe",
"WaiterId": "W001",
"TableNumber": "5",
"SpecialInstruction": "Rush Order",
"Gangs": [
{
"Id": 1,
"Name": "Starters",
"Dishes": [
{
"Number": 2,
"Name": "Caesar Salad",
"Modifications": {
"Removed": ["Croutons"],
"Added": ["Extra Dressing"]
},
"Comment": "No anchovies"
}
]
}
],
"Dishes": []
}
```
---
## Printer Profiles
Templates adapt to different printer capabilities using profiles.
### Built-in Profiles
| Printer | Profile ID | Line Width | Big Width | Red Support |
|---------|------------|------------|-----------|-------------|
| TM-T30III | `tm-t30iii` | 48 | 24 | No |
| TM-U220II | `tm-u220ii` | 33 | 20 | Yes |
### How Profiles Affect Output
1. **Line Width**: Separators and centered text use the profile's line width
2. **Big Width**: When `big` style is applied, centering uses the reduced width
3. **Red Support**: The `red` style only produces red output on supported printers
### Template Assignment
Templates are assigned to receipt types and printer profiles in `assignments.json`:
```json
{
"assignments": [
{
"receiptType": 1,
"profileId": "tm-t30iii",
"template": "kitchen-default.template"
},
{
"receiptType": 1,
"profileId": "tm-u220ii",
"template": "kitchen-u220.template"
},
{
"receiptType": 1,
"profileId": null,
"template": "kitchen-default.template"
}
],
"fallbackTemplate": "fallback.template"
}
```
**Resolution Priority:**
1. Exact match (receiptType + profileId)
2. Type-only match (receiptType, no profileId)
3. Fallback template
---
## Quick Reference
### Syntax Summary
| Syntax | Description |
|--------|-------------|
| `{path}` | Data binding |
| `{path:format}` | Formatted binding |
| `#style# text #` | Styled text |
| `---` | Dash separator |
| `===` | Equals separator |
| `***` | Star separator |
| `~~~` | Tilde separator |
| `@* comment` | Single-line comment |
| `@* comment *@` | Inline comment |
| `@*` ... `*@` | Multi-line comment block |
| `@if condition` | Start conditional |
| `@elseif condition` | Else-if branch |
| `@else` | Else branch |
| `@end` | End block |
| `@foreach var in collection` | Start loop |
| `@row` | Start row |
| `@row style1, style2` | Start row with styles |
| `@endrow` | End row |
| `\|width\|content` | Column definition |
| `\|width,align\|content` | Column with alignment |
### Style Reference
| Style | Effect |
|-------|--------|
| `bold` | Bold text |
| `big` | Double size |
| `tall` | Double height |
| `red` | Red color |
| `center` | Center align |
| `right` | Right align |
| `spacing:N` | Line spacing |
### Loop Variables
| Variable | Value |
|----------|-------|
| `_index` | 0, 1, 2, ... |
| `_number` | 1, 2, 3, ... |
| `_first` | true/false |
| `_last` | true/false |
| `_count` | total count |