🖨️ esc/pos implementation in gleam

refactor: api tweaks

okk.moe 3d06102b 7feaf208

verified
+155 -90
+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) 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) 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
··· 1 import escpos/protocol 2 import gleam/result 3 import mug ··· 8 CommandBuffer(data: BitArray) 9 } 10 11 pub opaque type Printer { 12 NetworkPrinter(socket: mug.Socket) 13 UsbPrinter(device: String) 14 } 15 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 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) 30 Error(err) -> Error(UsbDeviceError(err)) 31 } 32 } 33 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 51 pub fn print(cb: CommandBuffer, printer: Printer) -> Result(Nil, PrinterError) { 52 case printer { 53 NetworkPrinter(socket:) -> ··· 59 } 60 } 61 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) 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
··· 1 import gleam/bit_array 2 import gleam/int 3 4 pub type Justify { 5 Left 6 Center 7 Right 8 } 9 10 pub type Cut { 11 Partial 12 Full 13 } 14 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 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 48 pub const init = <<esc, "@">> 49 50 pub const lf = <<10>> 51 52 pub fn cut(cut: Cut) -> BitArray { 53 case cut { 54 Full -> <<gs, "V", 0>> ··· 56 } 57 } 58 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 68 pub fn justify(justify: Justify) -> BitArray { 69 case justify { 70 Left -> <<esc, "a", 0>> ··· 73 } 74 } 75 76 pub fn bold(on: Bool) -> BitArray { 77 case on { 78 True -> <<esc, "E", 1>> ··· 80 } 81 } 82 83 pub fn underline(on: Bool) -> BitArray { 84 case on { 85 True -> <<esc, "-", 1>> ··· 87 } 88 } 89 90 pub fn double_strike(on: Bool) -> BitArray { 91 case on { 92 True -> <<esc, "G", 1>> ··· 94 } 95 } 96 97 pub fn reverse(on: Bool) -> BitArray { 98 case on { 99 True -> <<gs, "B", 1>> ··· 101 } 102 } 103 104 pub fn upside_down(on: Bool) -> BitArray { 105 case on { 106 True -> <<esc, "{", 1>> ··· 108 } 109 } 110 111 pub fn smooth(on: Bool) -> BitArray { 112 case on { 113 True -> <<gs, "b", 1>> ··· 115 } 116 } 117 118 pub fn flip(on: Bool) -> BitArray { 119 case on { 120 True -> <<esc, "V", 1>> ··· 122 } 123 } 124 125 pub fn font(font: Font) -> BitArray { 126 case font { 127 FontA -> <<esc, "M", 0>> ··· 134 } 135 } 136 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` 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(),