Skip to content

Commit e3183ed

Browse files
authored
Add ZPL reverse conversion and line output (#14)
1 parent 2b91f29 commit e3183ed

7 files changed

Lines changed: 515 additions & 17 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ You can also try the package directly in your browser — the [WebAssembly b
1919
- convert `image.Image` values to complete ZPL labels with `ConvertToZPL`
2020
- generate raw `^GF` graphic fields with `ConvertToGraphicField`
2121
- choose between `ASCII`, `Binary`, `CompressedASCII` and `Z64` graphic field encodings
22+
- decode ZPL `^GF` graphic fields back to black and white images with `ConvertZPLToImage`
23+
- output black pixel runs as ZPL `^GB` line/box commands with `ConvertToZPLLines`
2224
- flatten images with alpha transparency against a white background with `FlattenImage`
2325
- compress ASCII graphic data with `CompressASCII`
2426
- position graphics on the label with `ConvertToZPLAt`
@@ -134,6 +136,18 @@ zplFromFile, err := zplgfa.ConvertFileToZPL("label.png", zplgfa.CompressedASCII)
134136
gf := zplgfa.ConvertToGraphicField(flat, zplgfa.ASCII)
135137
```
136138

139+
### Convert ZPL graphics back to an image
140+
141+
```go
142+
img, err := zplgfa.ConvertZPLToImage(zpl)
143+
```
144+
145+
### Output lines instead of a graphic field
146+
147+
```go
148+
zpl := zplgfa.ConvertToZPLLines(flat)
149+
```
150+
137151
## test and benchmark
138152

139153
Run the full test suite:

cmd/wasm/main.go

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func graphicTypeFromString(s string) zplgfa.GraphicType {
3333
}
3434
}
3535

36+
func isLineOutput(s string) bool {
37+
return strings.ToUpper(strings.TrimSpace(s)) == "LINES"
38+
}
39+
3640
// jsUint8ArrayToBytes copies a JavaScript Uint8Array into a Go []byte.
3741
func jsUint8ArrayToBytes(arr js.Value) []byte {
3842
length := arr.Get("length").Int()
@@ -50,8 +54,7 @@ func makeError(format string, a ...interface{}) map[string]interface{} {
5054

5155
// convertImage is the main entry point exported to JavaScript.
5256
//
53-
// JS signature: zplgfaConvert(bytes: Uint8Array, graphicType?: string)
54-
// => {zpl, width, height} | {error}
57+
// JS signature: zplgfaConvert(bytes: Uint8Array, graphicType?: string), returning {zpl, width, height} or {error}.
5558
func convertImage(this js.Value, args []js.Value) interface{} {
5659
if len(args) < 1 {
5760
return makeError("zplgfaConvert: expected at least one argument (Uint8Array)")
@@ -66,13 +69,28 @@ func convertImage(this js.Value, args []js.Value) interface{} {
6669
}
6770

6871
gt := zplgfa.CompressedASCII
72+
lines := false
6973
if len(args) >= 2 && args[1].Type() == js.TypeString {
70-
gt = graphicTypeFromString(args[1].String())
71-
}
72-
73-
zpl, err := zplgfa.ConvertReaderToZPL(bytes.NewReader(data), gt)
74-
if err != nil {
75-
return makeError("zplgfaConvert: %s", err)
74+
outputType := args[1].String()
75+
lines = isLineOutput(outputType)
76+
if !lines {
77+
gt = graphicTypeFromString(outputType)
78+
}
79+
}
80+
81+
var zpl string
82+
if lines {
83+
img, _, err := image.Decode(bytes.NewReader(data))
84+
if err != nil {
85+
return makeError("zplgfaConvert: %s", err)
86+
}
87+
zpl = zplgfa.ConvertToZPLLines(zplgfa.FlattenImage(img))
88+
} else {
89+
var err error
90+
zpl, err = zplgfa.ConvertReaderToZPL(bytes.NewReader(data), gt)
91+
if err != nil {
92+
return makeError("zplgfaConvert: %s", err)
93+
}
7694
}
7795

7896
// Decode the config again purely to expose width/height to the UI;
@@ -93,8 +111,7 @@ func convertImage(this js.Value, args []js.Value) interface{} {
93111
// plus width and height, allowing the in-browser editor to send canvas pixels
94112
// directly without re-encoding to PNG first.
95113
//
96-
// JS signature: zplgfaConvertRGBA(rgba: Uint8Array, width: number, height: number, graphicType?: string)
97-
// => {zpl, width, height} | {error}
114+
// JS signature: zplgfaConvertRGBA(rgba: Uint8Array, width: number, height: number, graphicType?: string), returning {zpl, width, height} or {error}.
98115
func convertRGBA(this js.Value, args []js.Value) interface{} {
99116
if len(args) < 3 {
100117
return makeError("zplgfaConvertRGBA: expected (rgba, width, height[, graphicType])")
@@ -119,12 +136,22 @@ func convertRGBA(this js.Value, args []js.Value) interface{} {
119136
}
120137

121138
gt := zplgfa.CompressedASCII
139+
lines := false
122140
if len(args) >= 4 && args[3].Type() == js.TypeString {
123-
gt = graphicTypeFromString(args[3].String())
141+
outputType := args[3].String()
142+
lines = isLineOutput(outputType)
143+
if !lines {
144+
gt = graphicTypeFromString(outputType)
145+
}
124146
}
125147

126148
flat := zplgfa.FlattenImage(img)
127-
zpl := zplgfa.ConvertToZPL(flat, gt)
149+
var zpl string
150+
if lines {
151+
zpl = zplgfa.ConvertToZPLLines(flat)
152+
} else {
153+
zpl = zplgfa.ConvertToZPL(flat, gt)
154+
}
128155

129156
return map[string]interface{}{
130157
"zpl": zpl,

cmd/zplgfa/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ or via the integrated network capability:
2323
zplgfa -file label.png -ip 192.168.178.42
2424
```
2525

26+
You can output black pixel runs as ZPL line/box commands instead of a `^GF` graphic field:
27+
28+
```sh
29+
zplgfa -file label.png -lines
30+
```
31+
32+
Or convert a ZPL file containing a `^GF` field back to PNG:
33+
34+
```sh
35+
zplgfa -file label.zpl -decode -out label.png
36+
```
37+
2638
You can also use some effects, e.g. blur:
2739

2840
```sh

cmd/zplgfa/main.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"image"
77
_ "image/gif"
88
_ "image/jpeg"
9-
_ "image/png"
9+
"image/png"
1010
"log"
1111
"os"
1212
"strings"
@@ -59,20 +59,24 @@ func handleZebraCommands(cmd, ip, port string) bool {
5959
return false
6060
}
6161

62-
func parseFlags() (string, string, string, string, string, string, float64) {
63-
var filename, zebraCmd, graphicType, imageEdit, ip, port string
62+
func parseFlags() (string, string, string, string, string, string, string, float64, bool, bool) {
63+
var filename, zebraCmd, graphicType, imageEdit, ip, port, output string
6464
var resizeFactor float64
65+
var lines, decode bool
6566

6667
flag.StringVar(&filename, "file", "", "filename to convert to zpl")
6768
flag.StringVar(&zebraCmd, "cmd", "", "send special command to printer [cancel,calib,feed,info,config,diag]")
6869
flag.StringVar(&graphicType, "type", "CompressedASCII", "type of graphic field encoding [ASCII,Binary,CompressedASCII,Z64]")
6970
flag.StringVar(&imageEdit, "edit", "", "manipulate the image [invert,monochrome]")
7071
flag.StringVar(&ip, "ip", "", "send zpl to printer")
7172
flag.StringVar(&port, "port", "9100", "network port of printer")
73+
flag.StringVar(&output, "out", "", "output filename for decoded PNG")
7274
flag.Float64Var(&resizeFactor, "resize", 1.0, "zoom/resize the image")
75+
flag.BoolVar(&lines, "lines", false, "output black pixel runs as ZPL line commands instead of a graphic field")
76+
flag.BoolVar(&decode, "decode", false, "convert a ZPL file containing a ^GF field to PNG")
7377

7478
flag.Parse()
75-
return filename, zebraCmd, graphicType, imageEdit, ip, port, resizeFactor
79+
return filename, zebraCmd, graphicType, imageEdit, ip, port, output, resizeFactor, lines, decode
7680
}
7781

7882
func openImageFile(filename string) (image.Image, image.Config, error) {
@@ -135,8 +139,28 @@ func getGraphicType(typeFlag string) zplgfa.GraphicType {
135139
}
136140
}
137141

142+
func decodeZPLFile(filename, output string) error {
143+
data, err := os.ReadFile(filename)
144+
if err != nil {
145+
return fmt.Errorf("could not read the file \"%s\": %s", filename, err)
146+
}
147+
img, err := zplgfa.ConvertZPLToImage(string(data))
148+
if err != nil {
149+
return err
150+
}
151+
if output == "" {
152+
return png.Encode(os.Stdout, img)
153+
}
154+
file, err := os.Create(output)
155+
if err != nil {
156+
return fmt.Errorf("could not create the file \"%s\": %s", output, err)
157+
}
158+
defer file.Close()
159+
return png.Encode(file, img)
160+
}
161+
138162
func main() {
139-
filename, zebraCmd, graphicTypeFlag, imageEdit, ip, port, resizeFactor := parseFlags()
163+
filename, zebraCmd, graphicTypeFlag, imageEdit, ip, port, output, resizeFactor, lines, decode := parseFlags()
140164

141165
if handleZebraCommands(zebraCmd, ip, port) && filename == "" {
142166
return
@@ -147,6 +171,13 @@ func main() {
147171
return
148172
}
149173

174+
if decode {
175+
if err := decodeZPLFile(filename, output); err != nil {
176+
log.Printf("Warning: %s\n", err)
177+
}
178+
return
179+
}
180+
150181
img, config, err := openImageFile(filename)
151182
if err != nil {
152183
log.Printf("Warning: %s\n", err)
@@ -157,6 +188,9 @@ func main() {
157188

158189
flat := zplgfa.FlattenImage(img)
159190
gfimg := zplgfa.ConvertToZPL(flat, getGraphicType(graphicTypeFlag))
191+
if lines {
192+
gfimg = zplgfa.ConvertToZPLLines(flat)
193+
}
160194

161195
if ip != "" {
162196
sendDataToZebra(ip, port, gfimg)

docs/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ <h2>2. Options</h2>
4848
<option value="CompressedASCII" selected>CompressedASCII</option>
4949
<option value="ASCII">ASCII</option>
5050
<option value="Binary">Binary</option>
51+
<option value="Lines">Lines (^GB)</option>
5152
</select>
5253
</div>
5354
<div class="row">
@@ -135,6 +136,7 @@ <h2>Export</h2>
135136
<option value="CompressedASCII" selected>CompressedASCII</option>
136137
<option value="ASCII">ASCII</option>
137138
<option value="Binary">Binary</option>
139+
<option value="Lines">Lines (^GB)</option>
138140
</select>
139141
</div>
140142
<div class="row">

0 commit comments

Comments
 (0)