package main import ( "bytes" "fmt" "image" "image/color" _ "image/gif" _ "image/jpeg" _ "image/png" "log" "math" "net/http" "strconv" "strings" "github.com/anthonynsimon/bild/blur" "github.com/anthonynsimon/bild/effect" "github.com/anthonynsimon/bild/segment" "github.com/nfnt/resize" "simonwaldherr.de/go/zplgfa" ) const maxUploadSize = 20 << 20 // 20 MB func getGraphicType(t string) zplgfa.GraphicType { switch strings.ToUpper(t) { case "ASCII": return zplgfa.ASCII case "BINARY": return zplgfa.Binary default: return zplgfa.CompressedASCII } } func invertImage(img image.Image) image.Image { b := img.Bounds() out := image.NewNRGBA(b) for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { r, g, bv, a := img.At(x, y).RGBA() out.Set(x, y, color.RGBA{ R: uint8((65535 - r) >> 8), G: uint8((65535 - g) >> 8), B: uint8((65535 - bv) >> 8), A: uint8(a >> 8), }) } } return out } func monochromeImage(img image.Image) image.Image { b := img.Bounds() out := image.NewNRGBA(b) for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { r, g, bv, a := img.At(x, y).RGBA() var v uint8 if r > math.MaxUint16/2 || g > math.MaxUint16/2 || bv > math.MaxUint16/2 { v = 255 } out.Set(x, y, color.RGBA{R: v, G: v, B: v, A: uint8(a >> 8)}) } } return out } func processImage(img image.Image, editFlag string, width, height uint) image.Image { if strings.Contains(editFlag, "monochrome") { img = monochromeImage(img) } bounds := img.Bounds() if strings.Contains(editFlag, "blur") { img = blur.Gaussian(img, float64(bounds.Dx())/300) } if strings.Contains(editFlag, "edge") { img = effect.Sobel(img) } if strings.Contains(editFlag, "segment") { img = segment.Threshold(img, 128) } if strings.Contains(editFlag, "invert") { img = invertImage(img) } if width > 0 || height > 0 { img = resize.Resize(width, height, img, resize.MitchellNetravali) } return img } func convertHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if err := r.ParseMultipartForm(maxUploadSize); err != nil { http.Error(w, "file too large", http.StatusBadRequest) return } file, _, err := r.FormFile("file") if err != nil { http.Error(w, "missing file", http.StatusBadRequest) return } defer file.Close() img, _, err := image.Decode(file) if err != nil { http.Error(w, fmt.Sprintf("cannot decode image: %s", err), http.StatusBadRequest) return } var w2, h2 uint if v, err := strconv.ParseUint(r.FormValue("width"), 10, 32); err == nil { w2 = uint(v) } if v, err := strconv.ParseUint(r.FormValue("height"), 10, 32); err == nil { h2 = uint(v) } editFlag := r.FormValue("edit") graphicType := getGraphicType(r.FormValue("type")) img = processImage(img, editFlag, w2, h2) flat := zplgfa.FlattenImage(img) zpl := zplgfa.ConvertToZPL(flat, graphicType) w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Content-Disposition", `attachment; filename="label.zpl"`) fmt.Fprint(w, zpl) } func previewHandler(w http.ResponseWriter, r *http.Request) { // Returns image info (dimensions) before conversion if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if err := r.ParseMultipartForm(maxUploadSize); err != nil { http.Error(w, "file too large", http.StatusBadRequest) return } file, _, err := r.FormFile("file") if err != nil { http.Error(w, "missing file", http.StatusBadRequest) return } defer file.Close() var buf bytes.Buffer buf.ReadFrom(file) cfg, _, err := image.DecodeConfig(bytes.NewReader(buf.Bytes())) if err != nil { http.Error(w, "cannot decode image", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, `{"width":%d,"height":%d}`, cfg.Width, cfg.Height) } func main() { mux := http.NewServeMux() mux.HandleFunc("/convert", convertHandler) mux.HandleFunc("/preview", previewHandler) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(indexHTML)) }) addr := ":4543" log.Printf("zplgfa web UI listening on %s", addr) log.Fatal(http.ListenAndServe(addr, mux)) } const indexHTML = ` zplgfa – Image to ZPL Converter

zplgfa

Convert images to ZPL Graphic Fields for Zebra label printers

🖼️
Drop an image here or click to browse
PNG, JPEG, GIF — max 20 MB
Dimensions in
Printer DPI
ZPL Output
`