package printer import ( "errors" "image" "math" ) // PrinterDriver is the interface all label printer implementations must satisfy. // The PRT Qutie driver, mock driver, and any future printer use this interface. type PrinterDriver interface { // Connect opens the connection to the printer. Must be called before Print. Connect() error // Print sends a rendered label bitmap to the printer. // bitmap is raw 1-bit packed row-major data from ImageToRawBitmap. Print(bitmap []byte, width, height int) error // Disconnect closes the connection. Disconnect() error } var ( // ErrNoDevice is returned when no printer matching the VID/PID is found. ErrNoDevice = errors.New("printer: no device found matching VID/PID") // ErrNotConnected is returned when Print is called before Connect. ErrNotConnected = errors.New("printer: not connected") // ErrEmptyBitmap is returned when an empty or nil bitmap is passed to Print. ErrEmptyBitmap = errors.New("printer: bitmap is empty") ) // ImageToRawBitmap converts an image.Image to 1-bit packed row-major bitmap. // White pixels → 0, dark pixels → 1. Returns bytes, width, and height. func ImageToRawBitmap(img image.Image) ([]byte, int, int) { b := img.Bounds() w, h := b.Max.X-b.Min.X, b.Max.Y-b.Min.Y rowBytes := int(math.Ceil(float64(w) / 8.0)) out := make([]byte, rowBytes*h) for y := 0; y < h; y++ { for x := 0; x < w; x++ { r, g, bv, _ := img.At(b.Min.X+x, b.Min.Y+y).RGBA() // Luminance: pixel is dark if average < 50% of max (0xffff). lum := (r + g + bv) / 3 if lum < 0x7fff { byteIdx := y*rowBytes + x/8 out[byteIdx] |= 1 << (7 - uint(x%8)) } } } return out, w, h }