193 lines
4.4 KiB
Go
193 lines
4.4 KiB
Go
|
|
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 1–8)
|
|||
|
|
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 (1–16)
|
|||
|
|
// 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)
|
|||
|
|
}
|