# 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 |