Files
libgofunc/escpos_printer.go
2026-03-16 22:42:30 +06:30

193 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}