๐Ÿ–จ๏ธ esc/pos implementation in gleam

Print images with raster command #1

merged opened by kwando.tngl.sh targeting main from kwando.tngl.sh/escpos: main

This adds support for printing images using the older GS v 0 command.

I'm 100% not sure about the API so hints or suggestions are welcome!

Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:2p3kzzrop7zyt6vdgiczbtf4/sh.tangled.repo.pull/3mfy4nysgtf22
+106
Diff #1
+42
src/escpos/document.gleam
··· 40 40 LineFeed(Int) 41 41 Cut(protocol.Cut) 42 42 Image(image: image.PrintableImage) 43 + RasterImage(image: image.PrintableImage) 43 44 Styled(modifiers: Set(Modifier), commands: List(Command)) 44 45 Raw(BitArray) 45 46 } ··· 65 66 DoLineFeed(Int) 66 67 DoCut(protocol.Cut) 67 68 DoImage(image.PrintableImage) 69 + DoRasterImage(image.PrintableImage) 68 70 DoRaw(BitArray) 69 71 SetBold(Bool) 70 72 SetUnderline(Bool) ··· 183 185 Image(image) 184 186 } 185 187 188 + /// Prints a monochrome image prepared by the `image` module. This is using an older 189 + /// method to print images, useful for some printers that do not understand graphic buffer 190 + /// commands. 191 + /// 192 + /// ## Example 193 + /// 194 + /// ```gleam 195 + /// let assert Ok(pgm) = simplifile.read_bits(from: "./lucy.pgm") 196 + /// let assert Ok(img) = image.from_pgm(raw_pgm) 197 + /// let img = image.dither_bayer4x4(img, 0) 198 + /// 199 + /// document.render([ 200 + /// raster_image(img), 201 + /// cut(), 202 + /// ]) 203 + /// ``` 204 + pub fn image_raster(image: image.PrintableImage) -> Command { 205 + RasterImage(image) 206 + } 207 + 186 208 /// Performs a full paper cut. 187 209 pub fn cut() -> Command { 188 210 Cut(protocol.Full) ··· 353 375 do_build_ast( 354 376 rest, 355 377 [DoImage(i), ..acc], 378 + State(..state, new_line: True), 379 + ) 380 + } 381 + RasterImage(i) -> { 382 + let #(acc, state) = ensure_new_line(state, acc) 383 + do_build_ast( 384 + rest, 385 + [DoRasterImage(i), ..acc], 356 386 State(..state, new_line: True), 357 387 ) 358 388 } ··· 560 590 do_compile_ast(rest, bit_array.append(acc, protocol.justify(j))) 561 591 SetFont(f) -> 562 592 do_compile_ast(rest, bit_array.append(acc, protocol.font(f))) 593 + DoRasterImage(i) -> 594 + do_compile_ast( 595 + rest, 596 + bit_array.append( 597 + acc, 598 + protocol.image_to_raster( 599 + image.width(i), 600 + image.height(i), 601 + image.pixels(i), 602 + ), 603 + ), 604 + ) 563 605 } 564 606 } 565 607 }
+37
src/escpos/protocol.gleam
··· 239 239 pub fn print_graphics_buffer() -> BitArray { 240 240 <<gs, "(", "L", 2, 0, 48, 50>> 241 241 } 242 + 243 + /// Encodes a raster bit image print command (`GS v 0`). 244 + /// 245 + /// `width` is in dots (pixels) and must be a multiple of 8, since the 246 + /// `packed_pixels` payload is 1 bit per pixel packed into bytes (MSB first), 247 + /// with each byte representing 8 horizontal pixels. 248 + /// 249 + /// `height` is in dots (rows). 250 + /// 251 + /// The payload size must be exactly `(width / 8) * height` bytes. 252 + pub fn image_to_raster( 253 + width: Int, 254 + height: Int, 255 + packed_pixels: BitArray, 256 + ) -> BitArray { 257 + let width_bytes = width / 8 258 + let x_low = width_bytes % 256 259 + let x_high = width_bytes / 256 260 + let y_low = height % 256 261 + let y_high = height / 256 262 + 263 + << 264 + gs, 265 + "v", 266 + "0", 267 + 0, 268 + // m = 0 (normal mode) 269 + x_low, 270 + x_high, 271 + // xL/xH = width in bytes (width / 8), little-endian 272 + y_low, 273 + y_high, 274 + // yL/yH = height in dots, little-endian 275 + packed_pixels:bits, 276 + // raster data (1 bpp, MSB first) 277 + >> 278 + }
+27
src/escpos.gleam
··· 165 165 |> printer.append_to_buffer(protocol.print_graphics_buffer()) 166 166 } 167 167 168 + /// Prints a monochrome image prepared by the `image` module. This is using an older 169 + /// method to print images, useful for some printers that do not understand graphic buffer 170 + /// commands. 171 + /// 172 + /// ## Example 173 + /// 174 + /// ```gleam 175 + /// let assert Ok(pgm) = simplifile.read_bits(from: "./lucy.pgm") 176 + /// let assert Ok(img) = image.from_pgm(raw_pgm) 177 + /// let img = image.dither_bayer4x4(img, 0) 178 + /// 179 + /// escpos.new() 180 + /// |> escpos.image_raster(img) 181 + /// |> printer.print(printer) 182 + /// ``` 183 + pub fn image_raster( 184 + cb: CommandBuffer, 185 + image: image.PrintableImage, 186 + ) -> CommandBuffer { 187 + ensure_new_line(cb) 188 + |> append(protocol.image_to_raster( 189 + image.width(image), 190 + image.height(image), 191 + image.pixels(image), 192 + )) 193 + } 194 + 168 195 /// Appends raw bytes to the buffer. 169 196 pub fn raw(cb: CommandBuffer, data: BitArray) -> CommandBuffer { 170 197 printer.append_to_buffer(cb, data)

History

2 rounds 2 comments
sign up or login to add to the discussion
2 commits
expand
rasterized image support
address review comments
expand 1 comment

Thank you :)

pull request successfully merged
1 commit
expand
rasterized image support
expand 1 comment

Typo in escpos.gleam on line 169: "do" instead of "does" Also could you add the same disclaimer to the "document" api. In the protocol.gleam on line 265: There's a constant for gs so you can use that instead of 29. Also you can type "v" instead of 118 and "0" instead of 48. That should make the comments redundant.

I've already pushed the CommandBuffer change, so you'll need to rebase and use printer.append_to_buffer in escpos.gleam

Thank you so much for the contribution ๐Ÿ’•