15 KiB
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
- Overview
- Basic Text
- Data Bindings
- Styled Text
- Separators
- Comments
- Conditionals
- Loops
- Rows and Columns
- Complete Example
- 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
@directivesyntax
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:
{"PropertyName": "Hello"}
Output:
Hello
Nested Properties
Use dot notation to access nested objects.
{Customer.Name}
{Order.Items.0.Name}
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:
{"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:
{"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:
{"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:
nullor missing propertyfalse- 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:
{"Items": ["Apple", "Banana", "Cherry"]}
Output:
Apple
Banana
Cherry
Object Properties
@foreach product in Products
{product.Name} - {product.Price:F2}
@end
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, orcenter
Examples
Two Columns
@row
|30|{Name}|10,right|{Price:F2}
@endrow
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:
{
"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
- Line Width: Separators and centered text use the profile's line width
- Big Width: When
bigstyle is applied, centering uses the reduced width - Red Support: The
redstyle only produces red output on supported printers
Template Assignment
Templates are assigned to receipt types and printer profiles in assignments.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:
- Exact match (receiptType + profileId)
- Type-only match (receiptType, no profileId)
- 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 |