This commit is contained in:
Simon Waldherr
2018-10-19 19:52:47 +02:00
commit 07c1abe3d3
6 changed files with 458 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.zpl
*.png
cmd/zplgfa/zplgfa

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Simon Waldherr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

62
README.md Normal file
View File

@@ -0,0 +1,62 @@
# ZPLGFA Golang Package
The ZPLGFA Golang package implements some functions to convert PNG, JPEG and GIF files to ZPL compatible ^GF-elements ([Graphic Fields](https://www.zebra.com/us/en/support-downloads/knowledge-articles/gf-graphic-field-zpl-command.html)).
## install
1. [install Golang](https://golang.org/doc/install)
1. `go get simonwaldherr.de/go/zplgfa`
## example
```go
package main
import (
"simonwaldherr.de/go/zplgfa"
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"log"
"os"
)
func main() {
// open file
file, err := os.Open("label.png")
if err != nil {
log.Printf("Warning: could not open the file: %s\n", err)
return
}
defer file.Close()
// load image head information
config, format, err := image.DecodeConfig(file)
if err != nil {
log.Printf("Warning: image not compatible, format: %s, config: %v, error: %s\n", format, config, err)
}
// reset file pointer to the beginning of the file
file.Seek(0, 0)
// load and decode image
img, _, err := image.Decode(file)
if err != nil {
log.Printf("Warning: could not decode the file, %s\n", err)
return
}
// flatten image
flat := zplgfa.FlattenImage(img)
// convert image to zpl compatible type
gfimg := zplgfa.ConvertToZPL(flat, zplgfa.CompressedASCII)
// output zpl with graphic field date to stdout
fmt.Println(gfimg)
}
```

31
cmd/zplgfa/README.md Normal file
View File

@@ -0,0 +1,31 @@
# ZPLGFA CLI Tool
The ZPLGFA cli tool converts PNG, JPEG and GIF images to [ZPL](https://www.zebra.com/content/dam/zebra/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf) strings.
So if you need to print labels on a [ZPL](https://en.wikipedia.org/wiki/Zebra_(programming_language) compatible printer
(like the amazing [ZEBRA ZM400](https://amzn.to/2OD5S4n)), but don't have ZPL-templates, you can use this free tool.
## install
1. [install Golang](https://golang.org/doc/install)
1. `go install simonwaldherr.de/go/zplgfa/cmd/zplgfa`
## usage
So if your image file is `label.png` and the IP of your printer is `192.168.178.42` you can print via this command:
```sh
./zplgfa -file label.png | nc 192.168.178.42 9100
```
You can also use some effects, e.g. blur:
```sh
./zplgfa -file label.png -edit blur | nc 192.168.178.42 9100
```
The ZPLGFA is actually just a demo application for the ZPLGFA package,
if you need something for productive work, look at the source and build something, depending on your needs
## test
You have your ZPLs, but no ZEBRA printer? You can test almost all ZPL functionality at [labelary.com](http://labelary.com/viewer.html).

146
cmd/zplgfa/main.go Normal file
View File

@@ -0,0 +1,146 @@
package main
import (
"simonwaldherr.de/go/zplgfa"
"flag"
"fmt"
"github.com/anthonynsimon/bild/blur"
"github.com/anthonynsimon/bild/effect"
"github.com/anthonynsimon/bild/segment"
"github.com/nfnt/resize"
"image"
"image/color"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"log"
"math"
"os"
"strings"
)
func main() {
var filenameFlag string
var graphicTypeFlag string
var imageEditFlag string
var imageResizeFlag float64
var graphicType zplgfa.GraphicType
flag.StringVar(&filenameFlag, "file", "", "filename to convert to zpl")
flag.StringVar(&graphicTypeFlag, "type", "CompressedASCII", "type of graphic field encoding")
flag.StringVar(&imageEditFlag, "edit", "", "manipulate the image [invert,monochrome]")
flag.Float64Var(&imageResizeFlag, "resize", 1.0, "zoom/resize the image")
// load flag input arguments
flag.Parse()
// open file
file, err := os.Open(filenameFlag)
if err != nil {
log.Printf("Warning: could not open the file \"%s\": %s\n", filenameFlag, err)
return
}
defer file.Close()
// load image head information
config, format, err := image.DecodeConfig(file)
if err != nil {
log.Printf("Warning: image not compatible, format: %s, config: %v, error: %s\n", format, config, err)
}
// reset file pointer to the beginning of the file
file.Seek(0, 0)
// load and decode image
img, _, err := image.Decode(file)
if err != nil {
log.Printf("Warning: could not decode the file, %s\n", err)
return
}
// select graphic field type
switch graphicTypeFlag {
case "ASCII":
graphicType = zplgfa.ASCII
case "Binary":
graphicType = zplgfa.Binary
case "CompressedASCII":
graphicType = zplgfa.CompressedASCII
default:
graphicType = zplgfa.CompressedASCII
}
// apply image manipulation functions
if strings.Contains(imageEditFlag, "monochrome") {
img = editImageMonochrome(img)
}
if strings.Contains(imageEditFlag, "blur") {
img = blur.Gaussian(img, float64(config.Width)/300)
}
if strings.Contains(imageEditFlag, "edge") {
img = effect.Sobel(img)
}
if strings.Contains(imageEditFlag, "segment") {
img = segment.Threshold(img, 128)
}
if strings.Contains(imageEditFlag, "invert") {
img = editImageInvert(img)
}
// resize image
if imageResizeFlag != 1.0 {
img = resize.Resize(uint(float64(config.Width)*imageResizeFlag), uint(float64(config.Height)*imageResizeFlag), img, resize.MitchellNetravali)
}
// flatten image
flat := zplgfa.FlattenImage(img)
// convert image to zpl compatible type
gfimg := zplgfa.ConvertToZPL(flat, graphicType)
// output zpl with graphic field date to stdout
fmt.Println(gfimg)
}
type ImageSet interface {
Set(x, y int, c color.Color)
}
func editImageInvert(img image.Image) image.Image {
b := img.Bounds()
imgSet := img.(ImageSet)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
oldPixel := img.At(x, y)
r, g, b, a := oldPixel.RGBA()
r = 65535 - r
g = 65535 - g
b = 65535 - b
pixel := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
imgSet.Set(x, y, pixel)
}
}
return img
}
func editImageMonochrome(img image.Image) image.Image {
b := img.Bounds()
imgSet := img.(ImageSet)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
oldPixel := img.At(x, y)
r, g, b, a := oldPixel.RGBA()
if r > math.MaxUint16/2 || g > math.MaxUint16/2 || b > math.MaxUint16/2 {
r, g, b = 65535, 65535, 65535
} else {
r, g, b = 0, 0, 0
}
pixel := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
imgSet.Set(x, y, pixel)
}
}
return img
}

195
zplgfa.go Normal file
View File

@@ -0,0 +1,195 @@
package zplgfa
import (
"encoding/hex"
"fmt"
"image"
"image/color"
"math"
"strings"
)
type GraphicType int
const (
ASCII GraphicType = iota
Binary
CompressedASCII
)
// ConvertToZPL is just a wrapper for ConvertToGraphicField which also includes the ZPL
// starting code ^XA and ending code ^XZ, as wall as a Field Separator and Field Origin.
func ConvertToZPL(img image.Image, graphicType GraphicType) string {
return fmt.Sprintf("^XA,^FS\n^FO0,0\n%s^FS,^XZ\n", ConvertToGraphicField(img, graphicType))
}
func FlattenImage(source image.Image) *image.NRGBA {
size := source.Bounds().Size()
background := color.White
target := image.NewNRGBA(source.Bounds())
for y := 0; y < size.Y; y++ {
for x := 0; x < size.X; x++ {
p := source.At(x, y)
flat := flatten(p, background)
target.Set(x, y, flat)
}
}
return target
}
func flatten(input color.Color, background color.Color) color.Color {
source := color.NRGBA64Model.Convert(input).(color.NRGBA64)
r, g, b, a := source.RGBA()
bg_r, bg_g, bg_b, _ := background.RGBA()
alpha := float32(a) / 0xffff
conv := func(c uint32, bg uint32) uint8 {
val := 0xffff - uint32((float32(bg) * alpha))
val = val | uint32(float32(c)*alpha)
return uint8(val >> 8)
}
c := color.NRGBA{
conv(r, bg_r),
conv(g, bg_g),
conv(b, bg_b),
uint8(0xff),
}
return c
}
func getRepeatCode(repeatCount int, char string) string {
repeatStr := ""
if repeatCount > 419 {
repeatCount -= 419
repeatStr += getRepeatCode(repeatCount, char)
repeatCount = 419
}
high := repeatCount / 20
low := repeatCount % 20
lowString := " GHIJKLMNOPQRSTUVWXY"
highString := " ghijklmnopqrstuvwxyz"
if high > 0 {
repeatStr += string(highString[high])
}
if low > 0 {
repeatStr += string(lowString[low])
}
repeatStr += char
return repeatStr
}
func compressASCII(in string) string {
in = strings.ToUpper(in)
var curChar string = ""
var lastChar string = ""
var lastCharSince int = 0
var output string = ""
var repCode string
for i := 0; i < len(in)+1; i++ {
if i == len(in) {
curChar = ""
if lastCharSince == 0 {
switch lastChar {
case "0":
output = ","
return output
case "F":
output = "!"
return output
}
}
} else {
curChar = string(in[i])
}
if lastChar != curChar {
if i-lastCharSince > 8 {
repCode = getRepeatCode(i-lastCharSince, lastChar)
output += repCode
} else {
for j := 0; j < i-lastCharSince; j++ {
output += lastChar
}
}
lastChar = curChar
lastCharSince = i
}
}
if output == "" {
output += getRepeatCode(len(in), lastChar)
}
return output
}
// ConvertToGraphicField converts an image.Image picture to a ZPL compatible Graphic Field.
// The ZPL ^GF (Graphic Field) supports various data formats, this package supports the
// normal ASCII encoded, as well as a RLE compressed ASCII format. It also supports the
// Binary Graphic Field format. The encoding can be choosen by the second argument.
func ConvertToGraphicField(source image.Image, graphicType GraphicType) string {
var gfType string
var lastLine string
size := source.Bounds().Size()
width := size.X / 8
height := size.Y
if size.Y%8 != 0 {
width = width + 1
}
var GraphicFieldData string
for y := 0; y < size.Y; y++ {
line := make([]uint8, width)
lineIndex := 0
index := uint8(0)
currentByte := line[lineIndex]
for x := 0; x < size.X; x++ {
index = index + 1
p := source.At(x, y)
lum := color.Gray16Model.Convert(p).(color.Gray16)
if lum.Y < math.MaxUint16/2 {
currentByte = currentByte | (1 << (8 - index))
}
if index >= 8 {
line[lineIndex] = currentByte
lineIndex++
if lineIndex < len(line) {
currentByte = line[lineIndex]
}
index = 0
}
}
hexstr := hex.EncodeToString(line)
switch graphicType {
case ASCII:
GraphicFieldData += fmt.Sprintln(hexstr)
case CompressedASCII:
curLine := compressASCII(hexstr)
if lastLine == curLine {
GraphicFieldData += ":"
} else {
GraphicFieldData += curLine
}
lastLine = curLine
case Binary:
GraphicFieldData += fmt.Sprintf("%s", line)
default:
graphicType = CompressedASCII
GraphicFieldData += fmt.Sprintln(compressASCII(hexstr))
}
}
if graphicType == ASCII || graphicType == CompressedASCII {
gfType = "A"
} else if graphicType == Binary {
gfType = "B"
}
return fmt.Sprintf("^GF%s,%d,%d,%d,\n%s", gfType, len(GraphicFieldData), width*height, width, GraphicFieldData)
}