tangled
alpha
login
or
join now
okk.moe
/
escpos
2
fork
atom
🖨️ esc/pos implementation in gleam
2
fork
atom
overview
issues
pulls
pipelines
refactor: api tweaks
okk.moe
1 month ago
3d06102b
7feaf208
verified
This commit was signed with the committer's
known signature
.
okk.moe
SSH Key Fingerprint:
SHA256:cVofqXFhnCrIkqo1ixFFvbU8h8MTjljdVwg5pBQ7wxY=
+155
-90
7 changed files
expand all
collapse all
unified
split
README.md
dev
escpos_dev.gleam
src
escpos
document.gleam
printer.gleam
protocol.gleam
escpos.gleam
test
escpos_test.gleam
+9
-9
README.md
···
20
The `escpos/document` module provides a high-level declarative API:
21
22
```gleam
23
-
import escpos/document.{bold, cut, justify, styled, writeln, Center}
24
import escpos/printer
25
26
pub fn main() {
27
let assert Ok(printer) = printer.connect("192.168.1.100", 9100)
28
29
-
document.build([
30
styled([justify(Center), bold()], [
31
-
writeln("Receipt"),
32
]),
33
-
writeln("Item 1 ... $5.00"),
34
-
writeln("Item 2 ... $3.50"),
35
cut(),
36
])
37
|> printer.print(printer)
···
51
52
escpos.new()
53
|> escpos.reset()
54
-
|> escpos.set_align(Center)
55
-
|> escpos.set_bold(True)
56
|> escpos.writeln("Receipt")
57
-
|> escpos.set_bold(False)
58
-
|> escpos.set_align(Left)
59
|> escpos.writeln("Item 1 ... $5.00")
60
|> escpos.writeln("Item 2 ... $3.50")
61
|> escpos.line_feed(3)
···
20
The `escpos/document` module provides a high-level declarative API:
21
22
```gleam
23
+
import escpos/document.{bold, cut, justify, styled, text_line, Center}
24
import escpos/printer
25
26
pub fn main() {
27
let assert Ok(printer) = printer.connect("192.168.1.100", 9100)
28
29
+
document.render([
30
styled([justify(Center), bold()], [
31
+
text_line("Receipt"),
32
]),
33
+
text_line("Item 1 ... $5.00"),
34
+
text_line("Item 2 ... $3.50"),
35
cut(),
36
])
37
|> printer.print(printer)
···
51
52
escpos.new()
53
|> escpos.reset()
54
+
|> escpos.align(Center)
55
+
|> escpos.bold(True)
56
|> escpos.writeln("Receipt")
57
+
|> escpos.bold(False)
58
+
|> escpos.align(Left)
59
|> escpos.writeln("Item 1 ... $5.00")
60
|> escpos.writeln("Item 2 ... $3.50")
61
|> escpos.line_feed(3)
+12
-9
dev/escpos_dev.gleam
···
13
imgpgm
14
// |> image.dither_ign
15
|> image.dither_bayer4x4(0)
16
-
// |> image.dither_bayer2x2(0)
17
18
-
let assert Ok(printer) = printer.connect("10.219.160.62", 9100)
0
19
20
-
escpos.new()
21
-
|> escpos.reset
22
-
|> escpos.image(imgpgm)
23
-
|> escpos.image(imgpbm)
24
-
|> escpos.line_feed(3)
25
-
|> escpos.cut
26
-
|> printer.print(printer)
0
0
27
}
···
13
imgpgm
14
// |> image.dither_ign
15
|> image.dither_bayer4x4(0)
16
+
// |> image.dither_bayer2x2(0)
17
18
+
// let assert Ok(printer) = printer.connect("10.219.160.62", 9100)
19
+
let assert Ok(printer) = printer.device("/dev/usb/lp0")
20
21
+
let assert Ok(_) =
22
+
escpos.new()
23
+
|> escpos.reset
24
+
|> escpos.image(imgpgm)
25
+
|> escpos.image(imgpbm)
26
+
|> escpos.line_feed(3)
27
+
|> escpos.cut
28
+
|> printer.print(printer)
29
+
// printer.disconnect(printer)
30
}
+16
-16
src/escpos.gleam
···
12
////
13
//// escpos.new()
14
//// |> escpos.reset()
15
-
//// |> escpos.set_align(Center)
16
-
//// |> escpos.set_bold(True)
17
//// |> escpos.writeln("Receipt")
18
-
//// |> escpos.set_bold(False)
19
-
//// |> escpos.set_align(Left)
20
//// |> escpos.writeln("Item 1 ... $5.00")
21
//// |> escpos.cut()
22
//// |> printer.print(my_printer)
···
53
}
54
55
/// Enables or disables bold text.
56
-
pub fn set_bold(cb: CommandBuffer, b: Bool) -> CommandBuffer {
57
append(cb, protocol.bold(b))
58
}
59
60
/// Enables or disables underlined text.
61
-
pub fn set_underline(cb: CommandBuffer, b: Bool) -> CommandBuffer {
62
append(cb, protocol.underline(b))
63
}
64
65
/// Enables or disables double-strike text.
66
-
pub fn set_double_strike(cb: CommandBuffer, b: Bool) -> CommandBuffer {
67
append(cb, protocol.double_strike(b))
68
}
69
70
/// Enables or disables reverse (white on black) text.
71
-
pub fn set_reverse(cb: CommandBuffer, b: Bool) -> CommandBuffer {
72
append(cb, protocol.reverse(b))
73
}
74
75
/// Enables or disables upside-down text.
76
-
pub fn set_upside_down(cb: CommandBuffer, b: Bool) -> CommandBuffer {
77
append(cb, protocol.upside_down(b))
78
}
79
80
/// Enables or disables character smoothing.
81
-
pub fn set_smooth(cb: CommandBuffer, b: Bool) -> CommandBuffer {
82
append(cb, protocol.smooth(b))
83
}
84
85
/// Enables or disables 180-degree rotation.
86
-
pub fn set_flip(cb: CommandBuffer, b: Bool) -> CommandBuffer {
87
append(cb, protocol.flip(b))
88
}
89
90
/// Sets the printer font.
91
-
pub fn set_font(cb: CommandBuffer, font: Font) -> CommandBuffer {
92
append(cb, protocol.font(font))
93
}
94
···
98
}
99
100
/// Sets text alignment (Left, Center, or Right).
101
-
pub fn set_align(cb: CommandBuffer, justify: Justify) -> CommandBuffer {
102
ensure_new_line(cb)
103
|> append(protocol.justify(justify))
104
}
105
106
/// Sets text size multiplier (1-8 for width and height).
107
-
pub fn set_text_size(
108
cb: CommandBuffer,
109
width: Int,
110
height: Int,
···
169
|> append(protocol.print_graphics_buffer())
170
}
171
172
-
/// Appends raw bytes to the buffer for custom commands.
173
-
pub fn custom(cb: CommandBuffer, data: BitArray) -> CommandBuffer {
174
append(cb, data)
175
}
176
···
12
////
13
//// escpos.new()
14
//// |> escpos.reset()
15
+
//// |> escpos.align(Center)
16
+
//// |> escpos.bold(True)
17
//// |> escpos.writeln("Receipt")
18
+
//// |> escpos.bold(False)
19
+
//// |> escpos.align(Left)
20
//// |> escpos.writeln("Item 1 ... $5.00")
21
//// |> escpos.cut()
22
//// |> printer.print(my_printer)
···
53
}
54
55
/// Enables or disables bold text.
56
+
pub fn bold(cb: CommandBuffer, b: Bool) -> CommandBuffer {
57
append(cb, protocol.bold(b))
58
}
59
60
/// Enables or disables underlined text.
61
+
pub fn underline(cb: CommandBuffer, b: Bool) -> CommandBuffer {
62
append(cb, protocol.underline(b))
63
}
64
65
/// Enables or disables double-strike text.
66
+
pub fn double_strike(cb: CommandBuffer, b: Bool) -> CommandBuffer {
67
append(cb, protocol.double_strike(b))
68
}
69
70
/// Enables or disables reverse (white on black) text.
71
+
pub fn reverse(cb: CommandBuffer, b: Bool) -> CommandBuffer {
72
append(cb, protocol.reverse(b))
73
}
74
75
/// Enables or disables upside-down text.
76
+
pub fn upside_down(cb: CommandBuffer, b: Bool) -> CommandBuffer {
77
append(cb, protocol.upside_down(b))
78
}
79
80
/// Enables or disables character smoothing.
81
+
pub fn smooth(cb: CommandBuffer, b: Bool) -> CommandBuffer {
82
append(cb, protocol.smooth(b))
83
}
84
85
/// Enables or disables 180-degree rotation.
86
+
pub fn flip(cb: CommandBuffer, b: Bool) -> CommandBuffer {
87
append(cb, protocol.flip(b))
88
}
89
90
/// Sets the printer font.
91
+
pub fn font(cb: CommandBuffer, font: Font) -> CommandBuffer {
92
append(cb, protocol.font(font))
93
}
94
···
98
}
99
100
/// Sets text alignment (Left, Center, or Right).
101
+
pub fn align(cb: CommandBuffer, justify: Justify) -> CommandBuffer {
102
ensure_new_line(cb)
103
|> append(protocol.justify(justify))
104
}
105
106
/// Sets text size multiplier (1-8 for width and height).
107
+
pub fn text_size(
108
cb: CommandBuffer,
109
width: Int,
110
height: Int,
···
169
|> append(protocol.print_graphics_buffer())
170
}
171
172
+
/// Appends raw bytes to the buffer.
173
+
pub fn raw(cb: CommandBuffer, data: BitArray) -> CommandBuffer {
174
append(cb, data)
175
}
176
+21
-21
src/escpos/document.gleam
···
6
//// ## Example
7
////
8
//// ```gleam
9
-
//// import escpos/document.{bold, cut, justify, styled, writeln, Center}
10
////
11
-
//// document.build([
12
//// styled([justify(Center), bold()], [
13
-
//// writeln("Receipt"),
14
//// ]),
15
-
//// writeln("Item 1 ... $5.00"),
16
//// cut(),
17
//// ])
18
//// ```
···
35
36
/// A command representing a print operation or content.
37
pub opaque type Command {
38
-
Write(String)
39
-
Writeln(String)
40
LineFeed(Int)
41
Cut(protocol.Cut)
42
Image(image: image.PrintableImage)
43
Styled(modifiers: Set(Modifier), commands: List(Command))
44
-
Custom(BitArray)
45
}
46
47
/// A style modifier that affects how text is rendered.
···
65
DoLineFeed(Int)
66
DoCut(protocol.Cut)
67
DoImage(image.PrintableImage)
68
-
DoCustom(BitArray)
69
SetBold(Bool)
70
SetUnderline(Bool)
71
SetDoubleStrike(Bool)
···
117
}
118
119
/// Compiles a list of commands into a binary command buffer ready for printing.
120
-
pub fn build(document: List(Command)) -> CommandBuffer {
121
upside_down_pass(document)
122
|> build_ast
123
|> compile_ast
···
131
}
132
133
/// Prints text without a trailing newline.
134
-
pub fn write(text: String) -> Command {
135
-
Write(text)
136
}
137
138
/// Prints text followed by a newline.
139
-
pub fn writeln(text: String) -> Command {
140
-
Writeln(text)
141
}
142
143
/// Advances the paper by the specified number of lines.
···
159
/// let assert Ok(img) = image.from_pgm(raw_pgm)
160
/// let img = image.dither_bayer4x4(img, 0)
161
///
162
-
/// document.build([
163
/// image(img),
164
/// cut(),
165
/// ])
···
179
}
180
181
/// Sends raw ESC/POS bytes to the printer.
182
-
pub fn custom(bytes: BitArray) -> Command {
183
-
Custom(bytes)
184
}
185
186
/// Bold text modifier.
···
310
[DoLineFeed(n), ..acc],
311
State(..state, new_line: True),
312
)
313
-
Write(x) ->
314
do_build_ast(
315
rest,
316
[DoWrite(x), ..acc],
317
State(..state, new_line: False),
318
)
319
-
Writeln(x) -> {
320
let new_acc =
321
list.prepend(acc, DoWrite(x)) |> list.prepend(DoLineFeed(1))
322
do_build_ast(rest, new_acc, State(..state, new_line: True))
···
329
State(..state, new_line: True),
330
)
331
}
332
-
Custom(b) ->
333
do_build_ast(
334
rest,
335
-
[DoCustom(b), ..acc],
336
State(..state, new_line: False),
337
)
338
}
···
512
DoLineFeed(n) ->
513
do_compile_ast(rest, bit_array.append(acc, protocol.line_feed(n)))
514
DoCut(c) -> do_compile_ast(rest, bit_array.append(acc, protocol.cut(c)))
515
-
DoCustom(b) -> do_compile_ast(rest, bit_array.append(acc, b))
516
SetBold(on) ->
517
do_compile_ast(rest, bit_array.append(acc, protocol.bold(on)))
518
SetUnderline(on) ->
···
6
//// ## Example
7
////
8
//// ```gleam
9
+
//// import escpos/document.{bold, cut, justify, styled, text_line, Center}
10
////
11
+
//// document.render([
12
//// styled([justify(Center), bold()], [
13
+
//// text_line("Receipt"),
14
//// ]),
15
+
//// text_line("Item 1 ... $5.00"),
16
//// cut(),
17
//// ])
18
//// ```
···
35
36
/// A command representing a print operation or content.
37
pub opaque type Command {
38
+
Text(String)
39
+
TextLine(String)
40
LineFeed(Int)
41
Cut(protocol.Cut)
42
Image(image: image.PrintableImage)
43
Styled(modifiers: Set(Modifier), commands: List(Command))
44
+
Raw(BitArray)
45
}
46
47
/// A style modifier that affects how text is rendered.
···
65
DoLineFeed(Int)
66
DoCut(protocol.Cut)
67
DoImage(image.PrintableImage)
68
+
DoRaw(BitArray)
69
SetBold(Bool)
70
SetUnderline(Bool)
71
SetDoubleStrike(Bool)
···
117
}
118
119
/// Compiles a list of commands into a binary command buffer ready for printing.
120
+
pub fn render(document: List(Command)) -> CommandBuffer {
121
upside_down_pass(document)
122
|> build_ast
123
|> compile_ast
···
131
}
132
133
/// Prints text without a trailing newline.
134
+
pub fn text(text: String) -> Command {
135
+
Text(text)
136
}
137
138
/// Prints text followed by a newline.
139
+
pub fn text_line(text: String) -> Command {
140
+
TextLine(text)
141
}
142
143
/// Advances the paper by the specified number of lines.
···
159
/// let assert Ok(img) = image.from_pgm(raw_pgm)
160
/// let img = image.dither_bayer4x4(img, 0)
161
///
162
+
/// document.render([
163
/// image(img),
164
/// cut(),
165
/// ])
···
179
}
180
181
/// Sends raw ESC/POS bytes to the printer.
182
+
pub fn raw(bytes: BitArray) -> Command {
183
+
Raw(bytes)
184
}
185
186
/// Bold text modifier.
···
310
[DoLineFeed(n), ..acc],
311
State(..state, new_line: True),
312
)
313
+
Text(x) ->
314
do_build_ast(
315
rest,
316
[DoWrite(x), ..acc],
317
State(..state, new_line: False),
318
)
319
+
TextLine(x) -> {
320
let new_acc =
321
list.prepend(acc, DoWrite(x)) |> list.prepend(DoLineFeed(1))
322
do_build_ast(rest, new_acc, State(..state, new_line: True))
···
329
State(..state, new_line: True),
330
)
331
}
332
+
Raw(b) ->
333
do_build_ast(
334
rest,
335
+
[DoRaw(b), ..acc],
336
State(..state, new_line: False),
337
)
338
}
···
512
DoLineFeed(n) ->
513
do_compile_ast(rest, bit_array.append(acc, protocol.line_feed(n)))
514
DoCut(c) -> do_compile_ast(rest, bit_array.append(acc, protocol.cut(c)))
515
+
DoRaw(b) -> do_compile_ast(rest, bit_array.append(acc, b))
516
SetBold(on) ->
517
do_compile_ast(rest, bit_array.append(acc, protocol.bold(on)))
518
SetUnderline(on) ->
+42
-6
src/escpos/printer.gleam
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
import escpos/protocol
2
import gleam/result
3
import mug
···
8
CommandBuffer(data: BitArray)
9
}
10
0
11
pub opaque type Printer {
12
NetworkPrinter(socket: mug.Socket)
13
UsbPrinter(device: String)
14
}
15
0
16
pub opaque type PrinterError {
17
ConnectionFailed(mug.ConnectError)
18
DisconnectionFailed(mug.Error)
···
20
NetworkPrintError(mug.Error)
21
UsbPrintError(simplifile.FileError)
22
UsbDeviceError(simplifile.FileError)
23
-
UsbDeviceNotFound
24
}
25
0
0
26
pub fn device(path: String) -> Result(Printer, PrinterError) {
27
-
case simplifile.is_file(path) {
28
-
Ok(True) -> Ok(UsbPrinter(device: path))
29
-
Ok(False) -> Error(UsbDeviceNotFound)
0
0
0
0
0
0
30
Error(err) -> Error(UsbDeviceError(err))
31
}
32
}
33
0
34
pub fn connect(ip: String, port: Int) -> Result(Printer, PrinterError) {
35
use socket <- result.try(
36
mug.new(ip, port)
37
-
|> mug.timeout(milliseconds: 500)
38
|> mug.connect()
39
|> result.map_error(ConnectionFailed),
40
)
···
47
Ok(NetworkPrinter(socket))
48
}
49
50
-
/// Sends the CommandBuffer to the printer
0
0
0
51
pub fn print(cb: CommandBuffer, printer: Printer) -> Result(Nil, PrinterError) {
52
case printer {
53
NetworkPrinter(socket:) ->
···
59
}
60
}
61
0
62
pub fn disconnect(printer: Printer) -> Result(Nil, PrinterError) {
63
case printer {
64
NetworkPrinter(socket:) ->
···
1
+
//// Functions for connecting to and communicating with ESC/POS printers.
2
+
////
3
+
//// Supports both USB (device file) and network (TCP socket) connections.
4
+
////
5
+
//// ## Example
6
+
////
7
+
//// ```gleam
8
+
//// // USB printer
9
+
//// let assert Ok(printer) = printer.device("/dev/usb/lp0")
10
+
////
11
+
//// // Network printer
12
+
//// let assert Ok(printer) = printer.connect("192.168.1.100", 9100)
13
+
////
14
+
//// escpos.new()
15
+
//// |> escpos.writeln("Hello!")
16
+
//// |> escpos.cut()
17
+
//// |> printer.print(printer)
18
+
////
19
+
//// // Close network printer socket
20
+
//// printer.disconnect(printer)
21
+
//// ```
22
+
23
import escpos/protocol
24
import gleam/result
25
import mug
···
30
CommandBuffer(data: BitArray)
31
}
32
33
+
/// A handle to a connected printer, either over USB or TCP.
34
pub opaque type Printer {
35
NetworkPrinter(socket: mug.Socket)
36
UsbPrinter(device: String)
37
}
38
39
+
/// Errors that can occur when connecting to or printing with a printer.
40
pub opaque type PrinterError {
41
ConnectionFailed(mug.ConnectError)
42
DisconnectionFailed(mug.Error)
···
44
NetworkPrintError(mug.Error)
45
UsbPrintError(simplifile.FileError)
46
UsbDeviceError(simplifile.FileError)
0
47
}
48
49
+
/// Opens a USB printer by its device file path (e.g. `/dev/usb/lp0`) and writes
50
+
/// the initialization command.
51
pub fn device(path: String) -> Result(Printer, PrinterError) {
52
+
case simplifile.file_info(path) {
53
+
Ok(_) -> {
54
+
use _ <- result.try(
55
+
simplifile.write_bits(path, protocol.init)
56
+
|> result.map_error(UsbDeviceError),
57
+
)
58
+
59
+
Ok(UsbPrinter(device: path))
60
+
}
61
Error(err) -> Error(UsbDeviceError(err))
62
}
63
}
64
65
+
/// Connects to a network printer over TCP and sends the initialization command.
66
pub fn connect(ip: String, port: Int) -> Result(Printer, PrinterError) {
67
use socket <- result.try(
68
mug.new(ip, port)
69
+
|> mug.timeout(milliseconds: 1000)
70
|> mug.connect()
71
|> result.map_error(ConnectionFailed),
72
)
···
79
Ok(NetworkPrinter(socket))
80
}
81
82
+
/// Sends a command buffer to the printer.
83
+
///
84
+
/// For network printers this writes to the TCP socket. For USB printers
85
+
/// this writes directly to the device file.
86
pub fn print(cb: CommandBuffer, printer: Printer) -> Result(Nil, PrinterError) {
87
case printer {
88
NetworkPrinter(socket:) ->
···
94
}
95
}
96
97
+
/// Closes the connection to a network printer. For USB printers this is a no-op.
98
pub fn disconnect(printer: Printer) -> Result(Nil, PrinterError) {
99
case printer {
100
NetworkPrinter(socket:) ->
+31
-5
src/escpos/protocol.gleam
···
0
0
0
0
0
0
1
import gleam/bit_array
2
import gleam/int
3
0
4
pub type Justify {
5
Left
6
Center
7
Right
8
}
9
0
10
pub type Cut {
11
Partial
12
Full
13
}
14
0
0
15
pub type Font {
16
FontA
17
FontB
···
22
SpecialFontB
23
}
24
25
-
/// most printers only support Monochrome
26
pub type ImageTone {
27
Monochrome
28
MultipleTone
29
}
30
0
31
pub type ImageScale {
32
Scale1x
33
Scale2x
34
}
35
36
-
/// most printers only support Color1 (Black)
37
pub type PrintColor {
38
Color1
39
Color2
···
45
46
const gs = 29
47
0
48
pub const init = <<esc, "@">>
49
0
50
pub const lf = <<10>>
51
0
52
pub fn cut(cut: Cut) -> BitArray {
53
case cut {
54
Full -> <<gs, "V", 0>>
···
56
}
57
}
58
0
59
pub fn line_feed(lines: Int) -> BitArray {
60
case lines {
61
l if l < 2 -> <<esc, "d", 1>>
···
64
}
65
}
66
67
-
/// requires to be on a new line to take effect
0
68
pub fn justify(justify: Justify) -> BitArray {
69
case justify {
70
Left -> <<esc, "a", 0>>
···
73
}
74
}
75
0
76
pub fn bold(on: Bool) -> BitArray {
77
case on {
78
True -> <<esc, "E", 1>>
···
80
}
81
}
82
0
83
pub fn underline(on: Bool) -> BitArray {
84
case on {
85
True -> <<esc, "-", 1>>
···
87
}
88
}
89
0
90
pub fn double_strike(on: Bool) -> BitArray {
91
case on {
92
True -> <<esc, "G", 1>>
···
94
}
95
}
96
0
97
pub fn reverse(on: Bool) -> BitArray {
98
case on {
99
True -> <<gs, "B", 1>>
···
101
}
102
}
103
0
104
pub fn upside_down(on: Bool) -> BitArray {
105
case on {
106
True -> <<esc, "{", 1>>
···
108
}
109
}
110
0
111
pub fn smooth(on: Bool) -> BitArray {
112
case on {
113
True -> <<gs, "b", 1>>
···
115
}
116
}
117
0
118
pub fn flip(on: Bool) -> BitArray {
119
case on {
120
True -> <<esc, "V", 1>>
···
122
}
123
}
124
0
125
pub fn font(font: Font) -> BitArray {
126
case font {
127
FontA -> <<esc, "M", 0>>
···
134
}
135
}
136
0
137
pub fn character_size(width: Int, height: Int) -> BitArray {
138
let w = int.clamp(width, min: 1, max: 8) |> int.subtract(1)
139
let h = int.clamp(height, min: 1, max: 8) |> int.subtract(1)
140
<<gs, "!", 0:1, w:3, 0:1, h:3>>
141
}
142
143
-
/// `gs ( L fn=112`
0
144
pub fn image_to_graphics_buffer(
145
data: BitArray,
146
width: Int,
···
209
>>
210
}
211
212
-
/// `gs ( L fn=50`
213
pub fn print_graphics_buffer() -> BitArray {
214
<<gs, "(", "L", 2, 0, 48, 50>>
215
}
···
1
+
//// Low-level ESC/POS command encoding.
2
+
////
3
+
//// Each function returns a `BitArray` containing the raw bytes for a single
4
+
//// ESC/POS command. These are used internally by the `escpos` module to
5
+
//// build command buffers.
6
+
7
import gleam/bit_array
8
import gleam/int
9
10
+
/// Text justification mode.
11
pub type Justify {
12
Left
13
Center
14
Right
15
}
16
17
+
/// Paper cut mode.
18
pub type Cut {
19
Partial
20
Full
21
}
22
23
+
/// Built-in printer font. Available fonts vary by printer model;
24
+
/// FontA and FontB are the most widely supported.
25
pub type Font {
26
FontA
27
FontB
···
32
SpecialFontB
33
}
34
35
+
/// Image tone mode. Most printers only support `Monochrome`.
36
pub type ImageTone {
37
Monochrome
38
MultipleTone
39
}
40
41
+
/// Image scaling factor for the graphics buffer.
42
pub type ImageScale {
43
Scale1x
44
Scale2x
45
}
46
47
+
/// Print color selection. Most printers only support `Color1` (black).
48
pub type PrintColor {
49
Color1
50
Color2
···
56
57
const gs = 29
58
59
+
/// Initialize printer command (`ESC @`).
60
pub const init = <<esc, "@">>
61
62
+
/// Line feed byte (`LF`).
63
pub const lf = <<10>>
64
65
+
/// Paper cut command (`GS V`).
66
pub fn cut(cut: Cut) -> BitArray {
67
case cut {
68
Full -> <<gs, "V", 0>>
···
70
}
71
}
72
73
+
/// Feeds the given number of lines, clamped to 1–255 (`ESC d`).
74
pub fn line_feed(lines: Int) -> BitArray {
75
case lines {
76
l if l < 2 -> <<esc, "d", 1>>
···
79
}
80
}
81
82
+
/// Sets text justification (`ESC a`). Must be at the start of a line
83
+
/// to take effect.
84
pub fn justify(justify: Justify) -> BitArray {
85
case justify {
86
Left -> <<esc, "a", 0>>
···
89
}
90
}
91
92
+
/// Enables or disables bold text (`ESC E`).
93
pub fn bold(on: Bool) -> BitArray {
94
case on {
95
True -> <<esc, "E", 1>>
···
97
}
98
}
99
100
+
/// Enables or disables underlined text (`ESC -`).
101
pub fn underline(on: Bool) -> BitArray {
102
case on {
103
True -> <<esc, "-", 1>>
···
105
}
106
}
107
108
+
/// Enables or disables double-strike text (`ESC G`).
109
pub fn double_strike(on: Bool) -> BitArray {
110
case on {
111
True -> <<esc, "G", 1>>
···
113
}
114
}
115
116
+
/// Enables or disables reverse (white on black) printing (`GS B`).
117
pub fn reverse(on: Bool) -> BitArray {
118
case on {
119
True -> <<gs, "B", 1>>
···
121
}
122
}
123
124
+
/// Enables or disables upside-down printing (`ESC {`).
125
pub fn upside_down(on: Bool) -> BitArray {
126
case on {
127
True -> <<esc, "{", 1>>
···
129
}
130
}
131
132
+
/// Enables or disables character smoothing (`GS b`).
133
pub fn smooth(on: Bool) -> BitArray {
134
case on {
135
True -> <<gs, "b", 1>>
···
137
}
138
}
139
140
+
/// Enables or disables 180-degree rotation (`ESC V`).
141
pub fn flip(on: Bool) -> BitArray {
142
case on {
143
True -> <<esc, "V", 1>>
···
145
}
146
}
147
148
+
/// Selects a built-in printer font (`ESC M`).
149
pub fn font(font: Font) -> BitArray {
150
case font {
151
FontA -> <<esc, "M", 0>>
···
158
}
159
}
160
161
+
/// Sets character width and height 1–8 (`GS !`).
162
pub fn character_size(width: Int, height: Int) -> BitArray {
163
let w = int.clamp(width, min: 1, max: 8) |> int.subtract(1)
164
let h = int.clamp(height, min: 1, max: 8) |> int.subtract(1)
165
<<gs, "!", 0:1, w:3, 0:1, h:3>>
166
}
167
168
+
/// Stores raster image data into the printer's graphics buffer
169
+
/// (`GS ( L`, fn=112).
170
pub fn image_to_graphics_buffer(
171
data: BitArray,
172
width: Int,
···
235
>>
236
}
237
238
+
/// Prints the contents of the graphics buffer (`GS ( L`, fn=50).
239
pub fn print_graphics_buffer() -> BitArray {
240
<<gs, "(", "L", 2, 0, 48, 50>>
241
}
+24
-24
test/escpos_test.gleam
···
14
15
pub fn upside_down_test() {
16
let input = [
17
-
document.write("Hello, World!"),
18
document.styled([document.upside_down()], [
19
-
document.write("Hello"),
20
-
document.write("Australia!"),
21
document.styled([document.bold()], [
22
-
document.write("Hello"),
23
-
document.write("Joe!"),
24
document.styled([document.upside_down()], [
25
-
document.write("Hello"),
26
-
document.write("New Zealand!"),
27
]),
28
]),
29
]),
30
]
31
let result = [
32
-
document.write("Hello, World!"),
33
document.styled([document.upside_down()], [
34
document.styled([document.bold()], [
35
document.styled([document.upside_down()], [
36
-
document.write("Hello"),
37
-
document.write("New Zealand!"),
38
]),
39
-
document.write("Joe!"),
40
-
document.write("Hello"),
41
]),
42
-
document.write("Australia!"),
43
-
document.write("Hello"),
44
]),
45
]
46
assert document.upside_down_pass(input) == result
···
66
67
let assert Ok(Nil) =
68
test_print(printer, "font B", fn(b) {
69
-
escpos.set_font(b, protocol.FontB)
70
|> escpos.write("Hello, World!")
71
|> escpos.reset_font
72
})
73
74
let assert Ok(Nil) =
75
test_print(printer, "font C", fn(b) {
76
-
escpos.set_font(b, protocol.FontC)
77
|> escpos.write("Hello, World!")
78
|> escpos.reset_font
79
})
80
81
let assert Ok(Nil) =
82
test_print(printer, "large text size", fn(b) {
83
-
escpos.set_text_size(b, 3, 3)
84
|> escpos.write("Hello, World!")
85
|> escpos.reset_text_size
86
})
···
96
let assert Ok(printer) = setup_printer()
97
98
let assert Ok(Nil) =
99
-
document.build([
100
-
document.writeln("hello"),
101
document.styled([document.bold()], [
102
-
document.writeln("world"),
103
document.styled([document.justify(protocol.Center)], [
104
-
document.writeln("center and bold"),
105
]),
106
]),
107
document.styled([document.justify(protocol.Right)], [
108
-
document.writeln("right"),
109
]),
110
document.styled([document.upside_down()], [
111
-
document.write("Hello"),
112
-
document.write("Australia!"),
113
]),
114
document.line_feed(3),
115
document.cut(),
···
14
15
pub fn upside_down_test() {
16
let input = [
17
+
document.text("Hello, World!"),
18
document.styled([document.upside_down()], [
19
+
document.text("Hello"),
20
+
document.text("Australia!"),
21
document.styled([document.bold()], [
22
+
document.text("Hello"),
23
+
document.text("Joe!"),
24
document.styled([document.upside_down()], [
25
+
document.text("Hello"),
26
+
document.text("New Zealand!"),
27
]),
28
]),
29
]),
30
]
31
let result = [
32
+
document.text("Hello, World!"),
33
document.styled([document.upside_down()], [
34
document.styled([document.bold()], [
35
document.styled([document.upside_down()], [
36
+
document.text("Hello"),
37
+
document.text("New Zealand!"),
38
]),
39
+
document.text("Joe!"),
40
+
document.text("Hello"),
41
]),
42
+
document.text("Australia!"),
43
+
document.text("Hello"),
44
]),
45
]
46
assert document.upside_down_pass(input) == result
···
66
67
let assert Ok(Nil) =
68
test_print(printer, "font B", fn(b) {
69
+
escpos.font(b, protocol.FontB)
70
|> escpos.write("Hello, World!")
71
|> escpos.reset_font
72
})
73
74
let assert Ok(Nil) =
75
test_print(printer, "font C", fn(b) {
76
+
escpos.font(b, protocol.FontC)
77
|> escpos.write("Hello, World!")
78
|> escpos.reset_font
79
})
80
81
let assert Ok(Nil) =
82
test_print(printer, "large text size", fn(b) {
83
+
escpos.text_size(b, 3, 3)
84
|> escpos.write("Hello, World!")
85
|> escpos.reset_text_size
86
})
···
96
let assert Ok(printer) = setup_printer()
97
98
let assert Ok(Nil) =
99
+
document.render([
100
+
document.text_line("hello"),
101
document.styled([document.bold()], [
102
+
document.text_line("world"),
103
document.styled([document.justify(protocol.Center)], [
104
+
document.text_line("center and bold"),
105
]),
106
]),
107
document.styled([document.justify(protocol.Right)], [
108
+
document.text_line("right"),
109
]),
110
document.styled([document.upside_down()], [
111
+
document.text("Hello"),
112
+
document.text("Australia!"),
113
]),
114
document.line_feed(3),
115
document.cut(),