//// A low-level imperative API for building ESC/POS printer commands. //// //// This module provides a builder-style interface where each function takes //// a `CommandBuffer` and returns a modified buffer. For a declarative //// alternative, see `escpos/document`. //// //// ## Example //// //// ```gleam //// import escpos.{Center, Left} //// import escpos/printer //// //// escpos.new() //// |> escpos.reset() //// |> escpos.align(Center) //// |> escpos.bold(True) //// |> escpos.writeln("Receipt") //// |> escpos.bold(False) //// |> escpos.align(Left) //// |> escpos.writeln("Item 1 ... $5.00") //// |> escpos.cut() //// |> printer.print(my_printer) //// ``` import escpos/image import escpos/printer.{type CommandBuffer, CommandBuffer} import escpos/protocol import gleam/bit_array import gleam/bool /// Text justification. Re-exported from `escpos/protocol`. pub type Justify = protocol.Justify /// Printer font. Re-exported from `escpos/protocol`. pub type Font = protocol.Font /// Creates a new empty command buffer. pub fn new() -> CommandBuffer { CommandBuffer(<<>>) } /// Writes text to the buffer. pub fn write(cb: CommandBuffer, text: String) -> CommandBuffer { append(cb, bit_array.from_string(text)) } /// Writes text to the buffer followed by a newline. pub fn writeln(cb: CommandBuffer, text: String) -> CommandBuffer { append(cb, bit_array.from_string(text)) |> append(protocol.lf) } /// Enables or disables bold text. pub fn bold(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.bold(b)) } /// Enables or disables underlined text. pub fn underline(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.underline(b)) } /// Enables or disables double-strike text. pub fn double_strike(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.double_strike(b)) } /// Enables or disables reverse (white on black) text. pub fn reverse(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.reverse(b)) } /// Enables or disables upside-down text. pub fn upside_down(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.upside_down(b)) } /// Enables or disables character smoothing. pub fn smooth(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.smooth(b)) } /// Enables or disables 180-degree rotation. pub fn flip(cb: CommandBuffer, b: Bool) -> CommandBuffer { append(cb, protocol.flip(b)) } /// Sets the printer font. pub fn font(cb: CommandBuffer, font: Font) -> CommandBuffer { append(cb, protocol.font(font)) } /// Resets the font to the default (FontA). pub fn reset_font(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.font(protocol.FontA)) } /// Sets text alignment (Left, Center, or Right). pub fn align(cb: CommandBuffer, justify: Justify) -> CommandBuffer { ensure_new_line(cb) |> append(protocol.justify(justify)) } /// Sets text size multiplier (1-8 for width and height). pub fn text_size( cb: CommandBuffer, width: Int, height: Int, ) -> CommandBuffer { append(cb, protocol.character_size(width, height)) } /// Resets text size to normal (1x1). pub fn reset_text_size(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.character_size(1, 1)) } /// Performs a full paper cut. pub fn cut(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.cut(protocol.Full)) } /// Performs a partial paper cut, leaving a small portion connected. pub fn partial_cut(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.cut(protocol.Partial)) } /// Appends a single newline. pub fn new_line(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.lf) } /// Feeds the specified number of lines. pub fn line_feed(cb: CommandBuffer, lines: Int) -> CommandBuffer { append(cb, protocol.line_feed(lines)) } /// Resets the printer to its initial state. pub fn reset(cb: CommandBuffer) -> CommandBuffer { append(cb, protocol.init) } /// Prints a monochrome image prepared by the `image` module. /// /// ## Example /// /// ```gleam /// let assert Ok(pgm) = simplifile.read_bits(from: "./lucy.pgm") /// let assert Ok(img) = image.from_pgm(raw_pgm) /// let img = image.dither_bayer4x4(img, 0) /// /// escpos.new() /// |> escpos.image(img) /// |> printer.print(printer) /// ``` pub fn image(cb: CommandBuffer, image: image.PrintableImage) -> CommandBuffer { ensure_new_line(cb) |> append(protocol.image_to_graphics_buffer( image.pixels(image), image.width(image), image.height(image), protocol.Monochrome, protocol.Scale1x, protocol.Scale1x, protocol.Color1, )) |> append(protocol.print_graphics_buffer()) } /// Appends raw bytes to the buffer. pub fn raw(cb: CommandBuffer, data: BitArray) -> CommandBuffer { append(cb, data) } fn append(cb: CommandBuffer, data: BitArray) -> CommandBuffer { bit_array.append(cb.data, data) |> CommandBuffer } fn ensure_new_line(cb: CommandBuffer) -> CommandBuffer { case is_new_line(cb) { True -> cb False -> new_line(cb) } } fn is_new_line(cb: CommandBuffer) -> Bool { use <- bool.guard(when: cb.data == <<>>, return: True) case bit_array.slice(from: cb.data, at: bit_array.byte_size(cb.data), take: -3) { Ok(<<27, "d", _>>) -> True Ok(<<_, _, 10>>) -> True _ -> False } }