Current section
Files
Jump to
Current section
Files
src/escpos.gleam
//// 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
}
}