homelabby/internal/printer/driver.go
Mikkel Georgsen 1156eff896 feat(04-03): PrinterDriver interface, MockDriver, PrtQutieDriver stub
- PrinterDriver interface: Connect/Print(bitmap,w,h)/Disconnect
- ImageToRawBitmap(): 1-bit packed row-major converter from image.Image
- MockDriver: saves PNG to SaveDir (/tmp default) for visual inspection
- PrtQutieDriver: stub returns ErrNoDevice — safe before hardware arrives
- ErrNoDevice, ErrNotConnected, ErrEmptyBitmap sentinel errors
2026-04-10 06:50:12 +00:00

49 lines
1.6 KiB
Go

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
}