label_printer/faxmachine/escpos/printer/printer.go

115 lines
2.2 KiB
Go

package printer
import (
"encoding/hex"
"fmt"
"io"
"log"
"os"
"strings"
"golang.org/x/sys/unix"
)
type Printer interface {
io.Reader
io.Writer
io.Closer
}
type usbPrinter struct {
*os.File
}
const (
FlagNone = 0
FlagDebug = 1 << 0
)
func OpenUSBPrinter(path string, flags int) (Printer, error) {
file, err := os.OpenFile(path, os.O_RDWR, 0)
if err != nil {
return nil, err
}
var printer Printer = usbPrinter{file}
if flags&FlagDebug != 0 {
printer = withDebugger(printer)
}
return printer, nil
}
func (usb usbPrinter) Write(p []byte) (int, error) {
// The printer often doens't like it if we use multiple write syscalls to
// transmit the data. usb.File.Write() doesn't seem to guarantee that it
// will only use a single syscall, so we manually call write instead.
return unix.Write(int(usb.Fd()), p)
}
type debugger struct {
Printer
lastError error
}
func withDebugger(printer Printer) Printer {
return &debugger{
Printer: printer,
lastError: nil,
}
}
func (d *debugger) Read(p []byte) (n int, err error) {
n, err = d.Printer.Read(p)
if err == io.EOF {
return
}
// don't spam the terminal with the same error again and again
if err != nil && d.lastError != nil && err.Error() == d.lastError.Error() {
return
}
d.lastError = err
var builder strings.Builder
fmt.Fprint(&builder, " \033[31m")
for i, b := range p[:n] {
if i > 0 {
fmt.Fprint(&builder, " ")
}
fmt.Fprintf(&builder, "%08b", b)
}
if err != nil {
if n > 0 {
fmt.Fprint(&builder, " ")
}
fmt.Fprintf(&builder, "read error: %v", err)
}
fmt.Fprintf(&builder, "\033[0m\n")
fmt.Fprint(os.Stderr, builder.String())
return
}
func (d *debugger) Write(p []byte) (n int, err error) {
n, err = d.Printer.Write(p)
printSize := len(p)
max := 20 * 8 * 2
if printSize > max {
printSize = max
}
fmt.Fprintf(os.Stderr, "\033[36m%s\033[0m", hex.Dump(p[:printSize]))
if len(p) > printSize {
fmt.Fprintf(os.Stderr, " \033[36m[%d bytes omitted]\033[0m\n", len(p)-printSize)
}
if err == nil && n == len(p) {
log.Printf("wrote %d bytes to printer\n", n)
} else {
log.Printf("only wrote %d/%d bytes to printer: %v\n", n, len(p), err)
}
return
}