Add Epson test page: text sizes, EAN-13, QR, and a raster image
deploy / deploy (push) Successful in 9s

ReceiptBuilder#image implements GS v 0 raster printing (no image library
needed for the demo -- a procedural 8px checkerboard proves it end to
end). Skipped attempting the printer's real-time clock: unsure the
TM-T20II actually has a battery-backed RTC (that's usually an option on
higher-end models), not worth guessing at an undocumented command.
This commit is contained in:
randy committed 2026-09-13 12:09:52 +02:00
1 parent 790d716d57
commit d2900361b3
3 files changed
+78

No files matched your search

+64
View File
@@ -107,6 +107,60 @@ helpers do
receipt.print!(PRINTER_DEVICE_EPSON) receipt.print!(PRINTER_DEVICE_EPSON)
end end
# Procedural 64x64 checkerboard (8px squares) -- no image library needed,
# just to prove GS v 0 raster printing works end to end.
def checkerboard_bitmap(size_px = 64, block_px = 8)
width_bytes = size_px / 8
bytes = "".b
size_px.times do |y|
row_block = y / block_px
width_bytes.times do |xb|
byte = 0
8.times do |bit|
x = xb * 8 + bit
col_block = x / block_px
byte |= (1 << (7 - bit)) if (row_block + col_block).even?
end
bytes << byte.chr
end
end
bytes
end
# Demonstrates most of what the printer can do: text sizes/styles, EAN-13,
# QR code, and a raster image -- mirrors /test-print's role for the Datamax.
def print_epson_test_page
receipt = ReceiptBuilder.new
receipt.align(:center).bold.size(:big).text("TM-T20II TEST\n").size(:normal).bold(false)
receipt.align(:left).text(('-' * 48) + "\n")
receipt.text("Normal text\n")
receipt.bold.text("Bold text\n").bold(false)
receipt.underline.text("Underlined text\n").underline(false)
receipt.size(:wide).text("Double width\n").size(:normal)
receipt.size(:high).text("Double height\n").size(:normal)
receipt.size(:big).text("Double both\n").size(:normal)
receipt.text(('-' * 48) + "\n")
receipt.align(:center)
receipt.text("EAN-13 barcode:\n")
receipt.ean13('2012345678902')
receipt.feed(1)
receipt.text("QR code:\n")
receipt.qr('https://git.priesol.net/randy/home-gitops')
receipt.feed(1)
receipt.text("Raster image:\n")
receipt.image(64, 64, checkerboard_bitmap)
receipt.feed(1)
receipt.align(:left).text(('-' * 48) + "\n")
receipt.text("Printed: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n")
receipt.feed(3).cut
receipt.print!(PRINTER_DEVICE_EPSON)
end
ESCPOS_MNEMONICS = { ESCPOS_MNEMONICS = {
'ESC' => "\x1B", 'GS' => "\x1D", 'FS' => "\x1C", 'DLE' => "\x10", 'ESC' => "\x1B", 'GS' => "\x1D", 'FS' => "\x1C", 'DLE' => "\x10",
'LF' => "\x0A", 'CR' => "\x0D", 'HT' => "\x09", 'NUL' => "\x00" 'LF' => "\x0A", 'CR' => "\x0D", 'HT' => "\x09", 'NUL' => "\x00"
@@ -313,6 +367,16 @@ get '/epson' do
erb :epson erb :epson
end end
post '/epson/test-print' do
begin
print_epson_test_page
flash 'Test page sent to Epson printer', 'success'
rescue => e
flash "Epson test print failed (#{e.message})", 'danger'
end
redirect '/epson'
end
post '/epson/print' do post '/epson/print' do
begin begin
print_receipt(title: params[:title].to_s, body: params[:body].to_s, qr: params[:qr].to_s) print_receipt(title: params[:title].to_s, body: params[:body].to_s, qr: params[:qr].to_s)
+10
View File
@@ -93,6 +93,16 @@ class ReceiptBuilder
self self
end end
# Raster bit image (GS v 0) -- bytes must already be packed 1bpp,
# row-major, (width_px/8 rounded up) bytes per row, MSB-first, 1=black.
def image(width_px, height_px, bytes)
width_bytes = (width_px + 7) / 8
@buffer << "\x1D\x76\x30\x00"
@buffer << [width_bytes % 256, width_bytes / 256, height_px % 256, height_px / 256].pack('C4')
@buffer << bytes
self
end
def feed(lines = 3) def feed(lines = 3)
@buffer << ("\n" * lines) @buffer << ("\n" * lines)
self self
+4
View File
@@ -15,6 +15,10 @@
<button class="btn btn-secondary" type="submit">Print</button> <button class="btn btn-secondary" type="submit">Print</button>
</form> </form>
<form action="/epson/test-print" method="POST" class="mt-3">
<button class="btn btn-outline-secondary" type="submit">Print test page (sizes, barcode, QR, image)</button>
</form>
<hr class="mt-4"> <hr class="mt-4">
<h5>Free text (ESC/POS mnemonic notation)</h5> <h5>Free text (ESC/POS mnemonic notation)</h5>