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