Files
libgofunc/escpos_printer.go

193 lines
4.4 KiB
Go
Raw Normal View History

2026-03-16 22:42:30 +06:30
package main
import (
"image"
"image/color"
"io"
)
// Printer wraps an io.Writer (USB file, TCP conn, etc.)
type Printer struct {
w io.Writer
}
func NewPrinter(w io.Writer) *Printer {
return &Printer{w: w}
}
func (p *Printer) Write(data []byte) error {
_, err := p.w.Write(data)
return err
}
// Print plain text
func (p *Printer) PrintText(text string) error {
return p.Write([]byte(text))
}
// New line
func (p *Printer) LF(num int) error {
for range num {
err := p.PrintText("\n")
if err != nil {
return err
}
}
return nil
}
// Bold text ON
func (p *Printer) BoldOn() error {
return p.Write([]byte{0x1b, 0x45, 0x01})
}
// Bold text OFF
func (p *Printer) BoldOff() error {
return p.Write([]byte{0x1b, 0x45, 0x00})
}
// Alignment: 0=left, 1=center, 2=right
func (p *Printer) Align(n int) error {
return p.Write([]byte{0x1b, 0x61, byte(n)})
}
// Cut paper
func (p *Printer) Cut() error {
return p.Write([]byte{0x1d, 0x56, 0x00})
}
// SelectFont chooses printer's built-in font (0=A, 1=B, 2=C if supported)
func (p *Printer) SelectFont(n int) error {
return p.Write([]byte{0x1b, 0x4d, byte(n)})
}
// SetFontSize scales text (x, y from 18)
func (p *Printer) SetFontSize(x, y int) error {
n := byte(((y - 1) << 4) | (x - 1))
return p.Write([]byte{0x1d, 0x21, n})
}
// Underline: 0=off, 1=thin, 2=thick
func (p *Printer) Underline(n int) error {
return p.Write([]byte{0x1b, 0x2d, byte(n)})
}
// UpsideDown: 0=off, 1=on
func (p *Printer) UpsideDown(n int) error {
return p.Write([]byte{0x1b, 0x7b, byte(n)})
}
// SetCharSpacing sets extra spacing between characters
func (p *Printer) SetCharSpacing(n int) error {
return p.Write([]byte{0x1b, 0x20, byte(n)})
}
// ---------------- IMAGE PRINTING ----------------
// convert to monochrome
func toMonochrome(img image.Image) *image.Gray {
bounds := img.Bounds()
gray := image.NewGray(bounds)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r, g, b, _ := img.At(x, y).RGBA()
grayValue := uint8((r + g + b) / 3 >> 8)
if grayValue > 128 {
gray.Set(x, y, color.White)
} else {
gray.Set(x, y, color.Black)
}
}
}
return gray
}
// ESC/POS: GS v 0
func escposRaster(img *image.Gray) []byte {
bounds := img.Bounds()
w := bounds.Dx()
h := bounds.Dy()
// Width must be in bytes (8 pixels per byte)
wBytes := (w + 7) / 8
data := []byte{}
// ESC/POS raster format: GS v 0 m xL xH yL yH [data]
// GS v 0
data = append(data, 0x1d, 0x76, 0x30, 0x00)
// xL, xH
data = append(data, byte(wBytes%256), byte(wBytes/256))
// yL, yH
data = append(data, byte(h%256), byte(h/256))
// Image data
for y := 0; y < h; y++ {
for xByte := 0; xByte < wBytes; xByte++ {
var b byte
for bit := 0; bit < 8; bit++ {
x := xByte*8 + bit
if x < w {
grayColor := img.GrayAt(x, y).Y
if grayColor < 128 {
// Black pixel = 1
b |= (1 << (7 - bit))
}
}
}
data = append(data, b)
}
}
return data
}
// PrintImage sends an image using GS v 0 (raster bit image)
func (p *Printer) PrintImage(img image.Image) error {
gray := toMonochrome(img)
data := escposRaster(gray)
return p.Write(data)
}
// PrintBarcode prints a CODE128 barcode (most supported)
func (p *Printer) PrintBarcode(data string) error {
// Select barcode height
p.Write([]byte{0x1d, 0x68, 100}) // height in dots
// Select barcode width
p.Write([]byte{0x1d, 0x77, 2}) // module width (2 = medium)
// HRI (human readable interpretation) below barcode
p.Write([]byte{0x1d, 0x48, 2})
// GS k m d1...dk NUL
cmd := []byte{0x1d, 0x6b, 73, byte(len(data))} // 73 = CODE128 auto
cmd = append(cmd, []byte(data)...)
return p.Write(cmd)
}
// ---------------- QR CODE ----------------
// PrintQRCode prints a QR code
func (p *Printer) PrintQRCode(data string, size int, ecLevel byte) error {
// size: module size (116)
// ecLevel: error correction (48=L, 49=M, 50=Q, 51=H)
// [1] Store QR data
storeLen := len(data) + 3
cmdStore := []byte{0x1d, 0x28, 0x6b, byte(storeLen % 256), byte(storeLen / 256), 49, 80, 48}
cmdStore = append(cmdStore, []byte(data)...)
// [2] Set module size
cmdSize := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 67, byte(size)}
// [3] Set error correction level
cmdErr := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 69, ecLevel}
// [4] Print
cmdPrint := []byte{0x1d, 0x28, 0x6b, 3, 0, 49, 81, 48}
p.Write(cmdSize)
p.Write(cmdErr)
p.Write(cmdStore)
return p.Write(cmdPrint)
}