Compare commits
4 Commits
9734cdd16e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
9db00dd64c
|
|||
|
3832ca749f
|
|||
|
f37c194ab8
|
|||
|
269e6514df
|
@@ -19,6 +19,7 @@ Runtime dependencies: Linux, a Brother QL label printer connected over USB
|
|||||||
$ go get -u https://janouch.name/sklad/cmd/sklad
|
$ go get -u https://janouch.name/sklad/cmd/sklad
|
||||||
|
|
||||||
You will need to bootstrap the database by writing a minimal 'db.json':
|
You will need to bootstrap the database by writing a minimal 'db.json':
|
||||||
|
|
||||||
....
|
....
|
||||||
{
|
{
|
||||||
"Password": "login-password",
|
"Password": "login-password",
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ func handle(w http.ResponseWriter, r *http.Request) {
|
|||||||
font.Font, params.Text, mediaInfo.PrintAreaPins, params.Scale)
|
font.Font, params.Text, mediaInfo.PrintAreaPins, params.Scale)
|
||||||
}
|
}
|
||||||
if r.FormValue("print") != "" {
|
if r.FormValue("print") != "" {
|
||||||
if err := printer.Print(img); err != nil {
|
if err := printer.Print(img, false); err != nil {
|
||||||
log.Println("print error:", err)
|
log.Println("print error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
|
|
||||||
var scale = flag.Int("scale", 1, "integer upscaling")
|
var scale = flag.Int("scale", 1, "integer upscaling")
|
||||||
var rotate = flag.Bool("rotate", false, "print sideways")
|
var rotate = flag.Bool("rotate", false, "print sideways")
|
||||||
|
var redblack = flag.Bool("redblack", false, "red and black print")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
@@ -82,7 +83,7 @@ func main() {
|
|||||||
log.Fatalln("the image is too high,", dy, ">", mi.PrintAreaLength, "pt")
|
log.Fatalln("the image is too high,", dy, ">", mi.PrintAreaLength, "pt")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.Print(img); err != nil {
|
if err := p.Print(img, *redblack); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ func printLabel(id string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return printer.Print(&imgutil.LeftRotate{Image: label.GenLabelForHeight(
|
return printer.Print(&imgutil.LeftRotate{Image: label.GenLabelForHeight(
|
||||||
labelFont, id, mediaInfo.PrintAreaPins, db.BDFScale)})
|
labelFont, id, mediaInfo.PrintAreaPins, db.BDFScale)}, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLabel(w http.ResponseWriter, r *http.Request) {
|
func handleLabel(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -1,5 +1,5 @@
|
|||||||
module janouch.name/sklad
|
module janouch.name/sklad
|
||||||
|
|
||||||
go 1.12
|
go 1.17
|
||||||
|
|
||||||
require github.com/boombuler/barcode v1.0.1
|
require github.com/boombuler/barcode v1.0.1
|
||||||
|
|||||||
106
ql/ql.go
106
ql/ql.go
@@ -120,9 +120,67 @@ const (
|
|||||||
printPins = printBytes * 8
|
printPins = printBytes * 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// pack packs a bool array into a byte array for the printer to print out.
|
||||||
|
func pack(data [printPins]bool, out *[]byte) {
|
||||||
|
for i := 0; i < printBytes; i++ {
|
||||||
|
var b byte
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
b <<= 1
|
||||||
|
if data[i*8+j] {
|
||||||
|
b |= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = append(*out, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeBitmapDataRB converts an image to the printer's red-black raster format.
|
||||||
|
func makeBitmapDataRB(src image.Image, margin, length int) []byte {
|
||||||
|
data, bounds := []byte{}, src.Bounds()
|
||||||
|
if bounds.Dy() > length {
|
||||||
|
bounds.Max.Y = bounds.Min.Y + length
|
||||||
|
}
|
||||||
|
if bounds.Dx() > printPins-margin {
|
||||||
|
bounds.Max.X = bounds.Min.X + printPins
|
||||||
|
}
|
||||||
|
|
||||||
|
redcells, blackcells := [printPins]bool{}, [printPins]bool{}
|
||||||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||||
|
length--
|
||||||
|
|
||||||
|
// The graphics needs to be inverted horizontally, iterating backwards.
|
||||||
|
offset := margin
|
||||||
|
for x := bounds.Max.X - 1; x >= bounds.Min.X; x-- {
|
||||||
|
r, g, b, a := src.At(x, y).RGBA()
|
||||||
|
redcells[offset] = r >= 0xc000 && g < 0x4000 && b < 0x4000 &&
|
||||||
|
a >= 0x8000
|
||||||
|
blackcells[offset] = r < 0x4000 && g < 0x4000 && b < 0x4000 &&
|
||||||
|
a >= 0x8000
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, 'w', 0x01, printBytes)
|
||||||
|
pack(blackcells, &data)
|
||||||
|
data = append(data, 'w', 0x02, printBytes)
|
||||||
|
pack(redcells, &data)
|
||||||
|
}
|
||||||
|
for ; length > 0; length-- {
|
||||||
|
data = append(data, 'w', 0x01, printBytes)
|
||||||
|
data = append(data, make([]byte, printBytes)...)
|
||||||
|
data = append(data, 'w', 0x02, printBytes)
|
||||||
|
data = append(data, make([]byte, printBytes)...)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
// makeBitmapData converts an image to the printer's raster format.
|
// makeBitmapData converts an image to the printer's raster format.
|
||||||
func makeBitmapData(src image.Image, margin, length int) (data []byte) {
|
func makeBitmapData(src image.Image, rb bool, margin, length int) []byte {
|
||||||
bounds := src.Bounds()
|
// It's a necessary nuisance, so just copy and paste.
|
||||||
|
if rb {
|
||||||
|
return makeBitmapDataRB(src, margin, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, bounds := []byte{}, src.Bounds()
|
||||||
if bounds.Dy() > length {
|
if bounds.Dy() > length {
|
||||||
bounds.Max.Y = bounds.Min.Y + length
|
bounds.Max.Y = bounds.Min.Y + length
|
||||||
}
|
}
|
||||||
@@ -138,30 +196,24 @@ func makeBitmapData(src image.Image, margin, length int) (data []byte) {
|
|||||||
offset := margin
|
offset := margin
|
||||||
for x := bounds.Max.X - 1; x >= bounds.Min.X; x-- {
|
for x := bounds.Max.X - 1; x >= bounds.Min.X; x-- {
|
||||||
r, g, b, a := src.At(x, y).RGBA()
|
r, g, b, a := src.At(x, y).RGBA()
|
||||||
pixels[offset] = r == 0 && g == 0 && b == 0 && a >= 0x8000
|
pixels[offset] = r < 0x4000 && g < 0x4000 && b < 0x4000 &&
|
||||||
|
a >= 0x8000
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
|
|
||||||
data = append(data, 'g', 0x00, printBytes)
|
data = append(data, 'g', 0x00, printBytes)
|
||||||
for i := 0; i < printBytes; i++ {
|
pack(pixels, &data)
|
||||||
var b byte
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
b <<= 1
|
|
||||||
if pixels[i*8+j] {
|
|
||||||
b |= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = append(data, b)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for ; length > 0; length-- {
|
for ; length > 0; length-- {
|
||||||
data = append(data, 'g', 0x00, printBytes)
|
data = append(data, 'g', 0x00, printBytes)
|
||||||
data = append(data, make([]byte, printBytes)...)
|
data = append(data, make([]byte, printBytes)...)
|
||||||
}
|
}
|
||||||
return
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePrintData(status *Status, image image.Image) (data []byte) {
|
// XXX: It would be preferrable to know for certain if this is a red-black tape,
|
||||||
|
// because the printer refuses to print on a mismatch.
|
||||||
|
func makePrintData(status *Status, image image.Image, rb bool) (data []byte) {
|
||||||
mediaInfo := GetMediaInfo(
|
mediaInfo := GetMediaInfo(
|
||||||
status.MediaWidthMM(),
|
status.MediaWidthMM(),
|
||||||
status.MediaLengthMM(),
|
status.MediaLengthMM(),
|
||||||
@@ -188,8 +240,17 @@ func makePrintData(status *Status, image image.Image) (data []byte) {
|
|||||||
mediaType = byte(0x0b)
|
mediaType = byte(0x0b)
|
||||||
}
|
}
|
||||||
|
|
||||||
data = append(data, 0x1b, 0x69, 0x7a, 0x02|0x04|0x40|0x80, mediaType,
|
const (
|
||||||
byte(status.MediaWidthMM()), byte(status.MediaLengthMM()),
|
flagValidMediaType = 0x02
|
||||||
|
flagValidMediaWidth = 0x04
|
||||||
|
flagValidMediaLength = 0x08
|
||||||
|
flagPriorityToQuality = 0x40
|
||||||
|
flagRecoveryAlwaysOn = 0x80
|
||||||
|
)
|
||||||
|
data = append(data, 0x1b, 0x69, 0x7a, flagValidMediaType|
|
||||||
|
flagValidMediaWidth|flagValidMediaLength|
|
||||||
|
flagPriorityToQuality|flagRecoveryAlwaysOn,
|
||||||
|
mediaType, byte(status.MediaWidthMM()), byte(status.MediaLengthMM()),
|
||||||
byte(dy), byte(dy>>8), byte(dy>>16), byte(dy>>24), 0, 0x00)
|
byte(dy), byte(dy>>8), byte(dy>>16), byte(dy>>24), 0, 0x00)
|
||||||
|
|
||||||
// Auto cut, each 1 label.
|
// Auto cut, each 1 label.
|
||||||
@@ -198,9 +259,13 @@ func makePrintData(status *Status, image image.Image) (data []byte) {
|
|||||||
|
|
||||||
// Cut at end (though it's the default).
|
// Cut at end (though it's the default).
|
||||||
// Not sure what it means, doesn't seem to have any effect to turn it off.
|
// Not sure what it means, doesn't seem to have any effect to turn it off.
|
||||||
data = append(data, 0x1b, 0x69, 0x4b, 0x08)
|
if rb {
|
||||||
|
data = append(data, 0x1b, 0x69, 0x4b, 0x08|0x01)
|
||||||
|
} else {
|
||||||
|
data = append(data, 0x1b, 0x69, 0x4b, 0x08)
|
||||||
|
}
|
||||||
|
|
||||||
if status.MediaLengthMM() != 0 {
|
if status.MediaLengthMM() == 0 {
|
||||||
// 3mm margins along the direction of feed. 0x23 = 35 dots, the minimum.
|
// 3mm margins along the direction of feed. 0x23 = 35 dots, the minimum.
|
||||||
data = append(data, 0x1b, 0x69, 0x64, 0x23, 0x00)
|
data = append(data, 0x1b, 0x69, 0x64, 0x23, 0x00)
|
||||||
} else {
|
} else {
|
||||||
@@ -213,7 +278,8 @@ func makePrintData(status *Status, image image.Image) (data []byte) {
|
|||||||
data = append(data, 0x4d, 0x00)
|
data = append(data, 0x4d, 0x00)
|
||||||
|
|
||||||
// The graphics data itself.
|
// The graphics data itself.
|
||||||
data = append(data, makeBitmapData(image, mediaInfo.SideMarginPins, dy)...)
|
bitmapData := makeBitmapData(image, rb, mediaInfo.SideMarginPins, dy)
|
||||||
|
data = append(data, bitmapData...)
|
||||||
|
|
||||||
// Print command with feeding.
|
// Print command with feeding.
|
||||||
return append(data, 0x1a)
|
return append(data, 0x1a)
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ var errErrorOccurred = errors.New("error occurred")
|
|||||||
var errUnexpectedStatus = errors.New("unexpected status")
|
var errUnexpectedStatus = errors.New("unexpected status")
|
||||||
var errUnknownMedia = errors.New("unknown media")
|
var errUnknownMedia = errors.New("unknown media")
|
||||||
|
|
||||||
func (p *Printer) Print(image image.Image) error {
|
func (p *Printer) Print(image image.Image, rb bool) error {
|
||||||
data := makePrintData(p.LastStatus, image)
|
data := makePrintData(p.LastStatus, image, rb)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return errUnknownMedia
|
return errUnknownMedia
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user