├── LICENSE ├── batterystatustype_string.go ├── cmd └── ptouchgo │ └── main.go ├── conn ├── conn.go ├── conn_generic.go └── usb │ └── conn.go ├── error2type_string.go ├── fontcolor_string.go ├── go.mod ├── go.sum ├── internal └── cmd │ └── enum │ └── enum.go ├── mediatype_string.go ├── model_string.go ├── notification_string.go ├── packbits.go ├── phasenumber_string.go ├── phasetypenumber_string.go ├── ptouch.go ├── statustype_string.go ├── tapecolor_string.go ├── tapewidth_enum.go └── tapewidth_string.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Katsuma Ito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /batterystatustype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix battery -type BatteryStatusType"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const _BatteryStatusType_name = "FullHalfLowChangeBatteriesAC" 8 | 9 | var _BatteryStatusType_index = [...]uint8{0, 4, 8, 11, 26, 28} 10 | 11 | func (i BatteryStatusType) String() string { 12 | if i < 0 || i >= BatteryStatusType(len(_BatteryStatusType_index)-1) { 13 | return "BatteryStatusType(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _BatteryStatusType_name[_BatteryStatusType_index[i]:_BatteryStatusType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /cmd/ptouchgo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/ka2n/ptouchgo" 10 | _ "github.com/ka2n/ptouchgo/conn/usb" 11 | ) 12 | 13 | var ( 14 | imagePath = flag.String("i", "", "Image path") 15 | devicePath = flag.String("d", "/dev/rfcomm0", `Device path(RFCOMM device path or "usb" or "usb://0x0000" or "tcp://192.168.100.1:9100")`) 16 | tapeWidth = flag.Uint("t", 24, "Tape width") 17 | debugMode = flag.Bool("debug", false, "Debug decoded image") 18 | dryRunMode = flag.Bool("dry", false, "not printing") 19 | ) 20 | 21 | var ( 22 | ser ptouchgo.Serial 23 | ) 24 | 25 | func main() { 26 | log.SetPrefix("ptouchgo: ") 27 | log.SetFlags(0) 28 | flag.Parse() 29 | 30 | err := mainCLI() 31 | if err != nil { 32 | log.Fatalln(err) 33 | } 34 | } 35 | 36 | func mainCLI() error { 37 | 38 | var err error 39 | if *imagePath == "" || *devicePath == "" { 40 | return fmt.Errorf("image file path and device path required") 41 | } 42 | 43 | tw := ptouchgo.TapeWidth(*tapeWidth) 44 | if !tw.Valid() { 45 | return fmt.Errorf("tapeWith only accespts 3.5,6,9,12,18,24") 46 | } 47 | 48 | // prepare data 49 | imgFile, err := os.Open(*imagePath) 50 | if err != nil { 51 | return err 52 | } 53 | defer imgFile.Close() 54 | 55 | data, bytesWidth, err := ptouchgo.LoadPNGImage(imgFile, tw) 56 | if err != nil { 57 | return fmt.Errorf("load image: %w", err) 58 | } 59 | rasterLines := len(data) / bytesWidth 60 | 61 | debug := *debugMode 62 | if debug { 63 | for i := 0; i < len(data); i += bytesWidth { 64 | to := i + bytesWidth 65 | if to > len(data) { 66 | to = len(data) 67 | } 68 | chunk := data[i:to] 69 | for _, c := range chunk { 70 | fmt.Printf("%08b", c) 71 | } 72 | fmt.Println() 73 | } 74 | } 75 | 76 | // Compless data 77 | packedData, err := ptouchgo.CompressImage(data, bytesWidth) 78 | if err != nil { 79 | return fmt.Errorf("convert image: %w", err) 80 | } 81 | 82 | if debug { 83 | log.Println("Image loaded") 84 | } 85 | 86 | // Open printer 87 | ser, err = ptouchgo.Open(*devicePath, *tapeWidth, debug) 88 | if err != nil { 89 | return fmt.Errorf("%s, %w", *devicePath, err) 90 | } 91 | defer ser.Close() 92 | 93 | err = ser.Reset() 94 | if err != nil { 95 | return err 96 | } 97 | 98 | err = ser.SetRasterMode() 99 | if err != nil { 100 | return err 101 | } 102 | 103 | // Set property 104 | err = ser.SetPrintProperty(rasterLines) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | err = ser.SetPrintMode(true, false) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | err = ser.SetExtendedMode(false, true, false, false, false) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | err = ser.SetFeedAmount(10) 120 | if err != nil { 121 | return err 122 | } 123 | 124 | err = ser.SetCompressionModeEnabled(true) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | if !*dryRunMode { 130 | err = ser.SendImage(packedData) 131 | if err != nil { 132 | return err 133 | } 134 | } 135 | 136 | if !*dryRunMode { 137 | err = ser.PrintAndEject() 138 | if err != nil { 139 | return err 140 | } 141 | } 142 | 143 | ser.Reset() 144 | return nil 145 | } 146 | -------------------------------------------------------------------------------- /conn/conn.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "sync" 7 | ) 8 | 9 | var ( 10 | driversMu sync.RWMutex 11 | drivers = make(map[string]Driver) 12 | ) 13 | 14 | func init() { 15 | Register("serial", DriverFunc(openSerial)) 16 | Register("tcp", DriverFunc(openTCP)) 17 | } 18 | 19 | // Driver is interface for connection backend 20 | type Driver interface { 21 | Open(address string) (io.ReadWriteCloser, error) 22 | } 23 | 24 | // Register new driver backend 25 | func Register(name string, driver Driver) { 26 | driversMu.Lock() 27 | defer driversMu.Unlock() 28 | if driver == nil { 29 | panic("serial: Register driver is nil") 30 | } 31 | if _, dup := drivers[name]; dup { 32 | panic("serial: Register called twice for driver " + name) 33 | } 34 | drivers[name] = driver 35 | } 36 | 37 | // Open connection with specific driver backend and address 38 | func Open(name, address string) (io.ReadWriteCloser, error) { 39 | driversMu.RLock() 40 | driver, ok := drivers[name] 41 | driversMu.RUnlock() 42 | if !ok { 43 | return nil, fmt.Errorf("serial: unknown driver %q", name) 44 | } 45 | return driver.Open(address) 46 | } 47 | 48 | // DriverFunc convert function into Driver like http.HandlerFunc 49 | type DriverFunc func(address string) (io.ReadWriteCloser, error) 50 | 51 | // Open call itsself as function 52 | func (f DriverFunc) Open(address string) (io.ReadWriteCloser, error) { 53 | return f(address) 54 | } 55 | -------------------------------------------------------------------------------- /conn/conn_generic.go: -------------------------------------------------------------------------------- 1 | package conn 2 | 3 | import ( 4 | "io" 5 | "net" 6 | 7 | "github.com/goburrow/serial" 8 | ) 9 | 10 | // openSerial for generic serial connection 11 | func openSerial(address string) (io.ReadWriteCloser, error) { 12 | return serial.Open(&serial.Config{ 13 | Address: address, 14 | BaudRate: 115200, 15 | StopBits: 1, 16 | Parity: "N", 17 | }) 18 | } 19 | 20 | func openTCP(address string) (io.ReadWriteCloser, error) { 21 | return net.Dial("tcp", address) 22 | } 23 | -------------------------------------------------------------------------------- /conn/usb/conn.go: -------------------------------------------------------------------------------- 1 | package usb 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/google/gousb" 12 | "github.com/ka2n/ptouchgo/conn" 13 | ) 14 | 15 | const ( 16 | brotherVendorID = 0x04f9 17 | productIDPTP700 = 0x2061 18 | productIDPTP750W = 0x2062 19 | productIDPTP710BT = 0x20af 20 | ) 21 | 22 | type USBSerial struct { 23 | ctx *gousb.Context 24 | dev *gousb.Device 25 | mu sync.Mutex 26 | readm sync.Mutex 27 | writem sync.Mutex 28 | input *gousb.InEndpoint 29 | output *gousb.OutEndpoint 30 | done func() 31 | } 32 | 33 | func init() { 34 | conn.Register("usb", conn.DriverFunc(OpenUSB)) 35 | } 36 | 37 | // OpenUSB open usb connection to device. if address is empty string, it will find pre defined device id. 38 | // address should formatted like "20af" or empty string. 39 | func OpenUSB(address string) (io.ReadWriteCloser, error) { 40 | var err error 41 | var ctx *gousb.Context 42 | var done func() 43 | var dev *gousb.Device 44 | var usbif *gousb.Interface 45 | var input *gousb.InEndpoint 46 | var output *gousb.OutEndpoint 47 | 48 | ctx = gousb.NewContext() 49 | ctx.Debug(10) 50 | 51 | if address != "" { 52 | if !strings.HasPrefix(address, "0x") { 53 | err = fmt.Errorf("invalid device address. address should \"0x0000\" form") 54 | goto handleError 55 | } 56 | 57 | var productID []byte 58 | productID, err = hex.DecodeString(address[2:]) 59 | if err != nil { 60 | goto handleError 61 | } 62 | dev, err = ctx.OpenDeviceWithVIDPID(brotherVendorID, gousb.ID(binary.BigEndian.Uint16(productID))) 63 | if err != nil { 64 | goto handleError 65 | } 66 | } else { 67 | dev, _ = ctx.OpenDeviceWithVIDPID(brotherVendorID, productIDPTP750W) 68 | if dev == nil { 69 | dev, _ = ctx.OpenDeviceWithVIDPID(brotherVendorID, productIDPTP700) 70 | } 71 | 72 | if dev == nil { 73 | dev, _ = ctx.OpenDeviceWithVIDPID(brotherVendorID, productIDPTP710BT) 74 | } 75 | } 76 | 77 | if dev == nil { 78 | err = fmt.Errorf("USB device not found") 79 | goto handleError 80 | } 81 | 82 | err = dev.SetAutoDetach(true) 83 | if err != nil { 84 | err = fmt.Errorf("set auto detach kernel driver: %w", err) 85 | goto handleError 86 | } 87 | 88 | usbif, done, err = dev.DefaultInterface() 89 | if err != nil { 90 | err = fmt.Errorf("get default interface: %w", err) 91 | goto handleError 92 | } 93 | 94 | input, err = usbif.InEndpoint(0x81) 95 | if err != nil { 96 | err = fmt.Errorf("open InEndpoint: %w", err) 97 | goto handleError 98 | } 99 | 100 | output, err = usbif.OutEndpoint(0x02) 101 | if err != nil { 102 | err = fmt.Errorf("open OutEndpoint: %w", err) 103 | goto handleError 104 | } 105 | 106 | return &USBSerial{ 107 | dev: dev, 108 | input: input, 109 | output: output, 110 | done: func() { 111 | done() 112 | dev.Close() 113 | ctx.Close() 114 | }, 115 | }, nil 116 | 117 | handleError: 118 | if done != nil { 119 | done() 120 | } 121 | if dev != nil { 122 | dev.Close() 123 | } 124 | if ctx != nil { 125 | ctx.Close() 126 | } 127 | return nil, err 128 | } 129 | 130 | func (s USBSerial) Close() error { 131 | s.mu.Lock() 132 | defer s.mu.Unlock() 133 | 134 | done := s.done 135 | s.done = nil 136 | s.input = nil 137 | s.output = nil 138 | done() 139 | return nil 140 | } 141 | 142 | func (s USBSerial) Write(b []byte) (int, error) { 143 | s.writem.Lock() 144 | defer s.writem.Unlock() 145 | return s.output.Write(b) 146 | } 147 | 148 | func (s USBSerial) Read(b []byte) (int, error) { 149 | s.readm.Lock() 150 | defer s.readm.Unlock() 151 | return s.input.Read(b) 152 | } 153 | -------------------------------------------------------------------------------- /error2type_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type Error2Type"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _Error2Type_name_0 = "Invalid media" 9 | _Error2Type_name_1 = "Cover open" 10 | _Error2Type_name_2 = "Too hot" 11 | ) 12 | 13 | func (i Error2Type) String() string { 14 | switch { 15 | case i == 1: 16 | return _Error2Type_name_0 17 | case i == 16: 18 | return _Error2Type_name_1 19 | case i == 32: 20 | return _Error2Type_name_2 21 | default: 22 | return "Error2Type(" + strconv.FormatInt(int64(i), 10) + ")" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fontcolor_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix fontColor -type FontColor"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _FontColor_name_0 = "WhiteOther" 9 | _FontColor_name_1 = "RedBlue" 10 | _FontColor_name_2 = "Black" 11 | _FontColor_name_3 = "Gold" 12 | _FontColor_name_4 = "FBlue" 13 | _FontColor_name_5 = "CleaningStencil" 14 | _FontColor_name_6 = "Invalid" 15 | ) 16 | 17 | var ( 18 | _FontColor_index_0 = [...]uint8{0, 5, 10} 19 | _FontColor_index_1 = [...]uint8{0, 3, 7} 20 | _FontColor_index_5 = [...]uint8{0, 8, 15} 21 | ) 22 | 23 | func (i FontColor) String() string { 24 | switch { 25 | case 1 <= i && i <= 2: 26 | i -= 1 27 | return _FontColor_name_0[_FontColor_index_0[i]:_FontColor_index_0[i+1]] 28 | case 4 <= i && i <= 5: 29 | i -= 4 30 | return _FontColor_name_1[_FontColor_index_1[i]:_FontColor_index_1[i+1]] 31 | case i == 8: 32 | return _FontColor_name_2 33 | case i == 10: 34 | return _FontColor_name_3 35 | case i == 98: 36 | return _FontColor_name_4 37 | case 240 <= i && i <= 241: 38 | i -= 240 39 | return _FontColor_name_5[_FontColor_index_5[i]:_FontColor_index_5[i+1]] 40 | case i == 255: 41 | return _FontColor_name_6 42 | default: 43 | return "FontColor(" + strconv.FormatInt(int64(i), 10) + ")" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ka2n/ptouchgo 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/disintegration/imaging v1.6.2 7 | github.com/goburrow/serial v0.1.0 8 | github.com/google/gousb v1.1.1 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 2 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 3 | github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA= 4 | github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA= 5 | github.com/google/gousb v1.1.1 h1:2sjwXlc0PIBgDnXtNxUrHcD/RRFOmAtRq4QgnFBE6xc= 6 | github.com/google/gousb v1.1.1/go.mod h1:b3uU8itc6dHElt063KJobuVtcKHWEfFOysOqBNzHhLY= 7 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= 8 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 9 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 10 | -------------------------------------------------------------------------------- /internal/cmd/enum/enum.go: -------------------------------------------------------------------------------- 1 | // Based on: https://github.com/golang/tools/tree/master/cmd/stringer 2 | // and https://github.com/favclip/jwg/blob/master/cmd/jwg/main.go 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2015 tv-asahi 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | package main 27 | 28 | import ( 29 | "bytes" 30 | "flag" 31 | "fmt" 32 | "go/ast" 33 | "go/build" 34 | "go/constant" 35 | "go/format" 36 | "go/importer" 37 | "go/parser" 38 | "go/token" 39 | "go/types" 40 | "io/ioutil" 41 | "log" 42 | "os" 43 | "path/filepath" 44 | "sort" 45 | "strings" 46 | ) 47 | 48 | var ( 49 | typeNames = flag.String("type", "", "comma-separated list of type names; must be set") 50 | output = flag.String("output", "", "output file name; default srcdir/_enum.go") 51 | ) 52 | 53 | // Usage is a replacement usage function for the flags package. 54 | func Usage() { 55 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) 56 | fmt.Fprintf(os.Stderr, "\tgoenum [flags] -type T [directory]\n") 57 | fmt.Fprintf(os.Stderr, "\tgoenum [flags] -type T files... # Must be a single package\n") 58 | fmt.Fprintf(os.Stderr, "Flags:\n") 59 | flag.PrintDefaults() 60 | } 61 | 62 | func main() { 63 | log.SetFlags(0) 64 | log.SetPrefix("goenum: ") 65 | flag.Usage = Usage 66 | flag.Parse() 67 | if len(*typeNames) == 0 { 68 | flag.Usage() 69 | os.Exit(2) 70 | } 71 | types := strings.Split(*typeNames, ",") 72 | 73 | // We accept either one directory or a list of files. Which do we have? 74 | args := flag.Args() 75 | if len(args) == 0 { 76 | // Default: process whole package in current directory. 77 | args = []string{"."} 78 | } 79 | 80 | // Parse the package once. 81 | var dir string 82 | g := Generator{} 83 | if len(args) == 1 && isDirectory(args[0]) { 84 | dir = args[0] 85 | g.parsePackageDir(args[0]) 86 | } else { 87 | dir = filepath.Dir(args[0]) 88 | g.parsePackageFiles(args) 89 | } 90 | 91 | // Print the header and package clause. 92 | g.Printf("// Code generated by \"goenum %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) 93 | g.Printf("\n") 94 | g.Printf("package %s", g.pkg.name) 95 | g.Printf("\n") 96 | // g.Printf("import \"strconv\"\n") // Used by all methods. 97 | 98 | // Run generate for each type. 99 | for _, typeName := range types { 100 | g.generate(typeName) 101 | } 102 | 103 | // Format the output. 104 | src := g.format() 105 | 106 | // Write to file. 107 | outputName := *output 108 | if outputName == "" { 109 | baseName := fmt.Sprintf("%s_enum.go", types[0]) 110 | outputName = filepath.Join(dir, strings.ToLower(baseName)) 111 | } 112 | err := ioutil.WriteFile(outputName, src, 0644) 113 | if err != nil { 114 | log.Fatalf("writing output: %s", err) 115 | } 116 | } 117 | 118 | // isDirectory reports whether the named file is a directory. 119 | func isDirectory(name string) bool { 120 | info, err := os.Stat(name) 121 | if err != nil { 122 | log.Fatal(err) 123 | } 124 | return info.IsDir() 125 | } 126 | 127 | // Generator holds the state of the analysis. Primarily used to buffer 128 | // the output for format.Source. 129 | type Generator struct { 130 | buf bytes.Buffer // Accumulated output. 131 | pkg *Package // Package we are scanning. 132 | } 133 | 134 | func (g *Generator) Printf(format string, args ...interface{}) { 135 | fmt.Fprintf(&g.buf, format, args...) 136 | } 137 | 138 | // File holds a single parsed file and associated data. 139 | type File struct { 140 | pkg *Package // Package to which this file belongs. 141 | file *ast.File // Parsed AST. 142 | // These fields are reset for each type being generated. 143 | typeName string // Name of the constant type. 144 | values []Value // Accumulator for constant values of that type. 145 | } 146 | 147 | type Package struct { 148 | dir string 149 | name string 150 | defs map[*ast.Ident]types.Object 151 | files []*File 152 | typesPkg *types.Package 153 | } 154 | 155 | func buildContext() *build.Context { 156 | ctx := build.Default 157 | return &ctx 158 | } 159 | 160 | // parsePackageDir parses the package residing in the directory. 161 | func (g *Generator) parsePackageDir(directory string) { 162 | pkg, err := buildContext().ImportDir(directory, 0) 163 | if err != nil { 164 | log.Fatalf("cannot process directory %s: %s", directory, err) 165 | } 166 | var names []string 167 | names = append(names, pkg.GoFiles...) 168 | names = append(names, pkg.CgoFiles...) 169 | // TODO: Need to think about constants in test files. Maybe write type_string_test.go 170 | // in a separate pass? For later. 171 | // names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. 172 | names = append(names, pkg.SFiles...) 173 | names = prefixDirectory(directory, names) 174 | g.parsePackage(directory, names, nil) 175 | } 176 | 177 | // parsePackageFiles parses the package occupying the named files. 178 | func (g *Generator) parsePackageFiles(names []string) { 179 | g.parsePackage(".", names, nil) 180 | } 181 | 182 | // prefixDirectory places the directory name on the beginning of each name in the list. 183 | func prefixDirectory(directory string, names []string) []string { 184 | if directory == "." { 185 | return names 186 | } 187 | ret := make([]string, len(names)) 188 | for i, name := range names { 189 | ret[i] = filepath.Join(directory, name) 190 | } 191 | return ret 192 | } 193 | 194 | // parsePackage analyzes the single package constructed from the named files. 195 | // If text is non-nil, it is a string to be used instead of the content of the file, 196 | // to be used for testing. parsePackage exits if there is an error. 197 | func (g *Generator) parsePackage(directory string, names []string, text interface{}) { 198 | var files []*File 199 | var astFiles []*ast.File 200 | g.pkg = new(Package) 201 | fs := token.NewFileSet() 202 | for _, name := range names { 203 | if !strings.HasSuffix(name, ".go") { 204 | continue 205 | } 206 | parsedFile, err := parser.ParseFile(fs, name, text, parser.ParseComments) 207 | if err != nil { 208 | log.Fatalf("parsing package: %s: %s", name, err) 209 | } 210 | astFiles = append(astFiles, parsedFile) 211 | files = append(files, &File{ 212 | file: parsedFile, 213 | pkg: g.pkg, 214 | }) 215 | } 216 | if len(astFiles) == 0 { 217 | log.Fatalf("%s: no buildable Go files", directory) 218 | } 219 | g.pkg.name = astFiles[0].Name.Name 220 | g.pkg.files = files 221 | g.pkg.dir = directory 222 | g.pkg.typeCheck(fs, astFiles) 223 | } 224 | 225 | // check type-checks the package so we can evaluate contants whose values we are printing. 226 | func (pkg *Package) typeCheck(fs *token.FileSet, astFiles []*ast.File) { 227 | pkg.defs = make(map[*ast.Ident]types.Object) 228 | config := types.Config{ 229 | IgnoreFuncBodies: true, // We only need to evaluate constants. 230 | Importer: importer.For("source", nil), 231 | FakeImportC: true, 232 | } 233 | info := &types.Info{ 234 | Defs: pkg.defs, 235 | } 236 | typesPkg, err := config.Check(pkg.dir, fs, astFiles, info) 237 | if err != nil { 238 | log.Fatalf("checking package: %s", err) 239 | } 240 | pkg.typesPkg = typesPkg 241 | } 242 | 243 | // generate produces the String method for the named type. 244 | func (g *Generator) generate(typeName string) { 245 | values := make([]Value, 0, 100) 246 | for _, file := range g.pkg.files { 247 | // Set the state for this run of the walker. 248 | file.typeName = typeName 249 | file.values = nil 250 | if file.file != nil { 251 | ast.Inspect(file.file, file.genDecl) 252 | values = append(values, file.values...) 253 | } 254 | } 255 | 256 | if len(values) == 0 { 257 | log.Fatalf("no values defined for type %s", typeName) 258 | } 259 | runs := splitIntoRuns(values) 260 | // The decision of which pattern to use depends on the number of 261 | // runs in the numbers. If there's only one, it's easy. For more than 262 | // one, there's a tradeoff between complexity and size of the data 263 | // and code vs. the simplicity of a map. A map takes more space, 264 | // but so does the code. The decision here (crossover at 10) is 265 | // arbitrary, but considers that for large numbers of runs the cost 266 | // of the linear scan in the switch might become important, and 267 | // rather than use yet another algorithm such as binary search, 268 | // we punt and use a map. In any case, the likelihood of a map 269 | // being necessary for any realistic example other than bitmasks 270 | // is very low. And bitmasks probably deserve their own analysis, 271 | // to be done some other day. 272 | switch { 273 | case len(runs) == 1: 274 | g.buildOneRun(runs, typeName) 275 | case len(runs) <= 10: 276 | g.buildMultipleRuns(runs, typeName) 277 | default: 278 | g.buildMap(runs, typeName) 279 | } 280 | } 281 | 282 | // splitIntoRuns breaks the values into runs of contiguous sequences. 283 | // For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}. 284 | // The input slice is known to be non-empty. 285 | func splitIntoRuns(values []Value) [][]Value { 286 | // We use stable sort so the lexically first name is chosen for equal elements. 287 | sort.Stable(byValue(values)) 288 | // Remove duplicates. Stable sort has put the one we want to print first, 289 | // so use that one. The String method won't care about which named constant 290 | // was the argument, so the first name for the given value is the only one to keep. 291 | // We need to do this because identical values would cause the switch or map 292 | // to fail to compile. 293 | j := 1 294 | for i := 1; i < len(values); i++ { 295 | if values[i].value != values[i-1].value { 296 | values[j] = values[i] 297 | j++ 298 | } 299 | } 300 | values = values[:j] 301 | runs := make([][]Value, 0, 10) 302 | for len(values) > 0 { 303 | // One contiguous sequence per outer loop. 304 | i := 1 305 | for i < len(values) && values[i].value == values[i-1].value+1 { 306 | i++ 307 | } 308 | runs = append(runs, values[:i]) 309 | values = values[i:] 310 | } 311 | return runs 312 | } 313 | 314 | // format returns the gofmt-ed contents of the Generator's buffer. 315 | func (g *Generator) format() []byte { 316 | src, err := format.Source(g.buf.Bytes()) 317 | if err != nil { 318 | // Should never happen, but can arise when developing this code. 319 | // The user can compile the output to see the error. 320 | log.Printf("warning: internal error: invalid Go generated: %s", err) 321 | log.Printf("warning: compile the package to analyze the error") 322 | return g.buf.Bytes() 323 | } 324 | return src 325 | } 326 | 327 | // Value represents a declared constant. 328 | type Value struct { 329 | name string // The name of the constant. 330 | // The value is stored as a bit pattern alone. The boolean tells us 331 | // whether to interpret it as an int64 or a uint64; the only place 332 | // this matters is when sorting. 333 | // Much of the time the str field is all we need; it is printed 334 | // by Value.String. 335 | value uint64 // Will be converted to int64 when needed. 336 | signed bool // Whether the constant is a signed type. 337 | str string // The string representation given by the "go/constant" package. 338 | } 339 | 340 | func (v *Value) String() string { 341 | return v.str 342 | } 343 | 344 | // byValue lets us sort the constants into increasing order. 345 | // We take care in the Less method to sort in signed or unsigned order, 346 | // as appropriate. 347 | type byValue []Value 348 | 349 | func (b byValue) Len() int { return len(b) } 350 | func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 351 | func (b byValue) Less(i, j int) bool { 352 | if b[i].signed { 353 | return int64(b[i].value) < int64(b[j].value) 354 | } 355 | return b[i].value < b[j].value 356 | } 357 | 358 | // genDecl processes one declaration clause. 359 | func (f *File) genDecl(node ast.Node) bool { 360 | decl, ok := node.(*ast.GenDecl) 361 | if !ok || decl.Tok != token.CONST { 362 | // We only care about const declarations. 363 | return true 364 | } 365 | // The name of the type of the constants we are declaring. 366 | // Can change if this is a multi-element declaration. 367 | typ := "" 368 | // Loop over the elements of the declaration. Each element is a ValueSpec: 369 | // a list of names possibly followed by a type, possibly followed by values. 370 | // If the type and value are both missing, we carry down the type (and value, 371 | // but the "go/types" package takes care of that). 372 | for _, spec := range decl.Specs { 373 | vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. 374 | if vspec.Type == nil && len(vspec.Values) > 0 { 375 | // "X = 1". With no type but a value, the constant is untyped. 376 | // Skip this vspec and reset the remembered type. 377 | typ = "" 378 | continue 379 | } 380 | if vspec.Type != nil { 381 | // "X T". We have a type. Remember it. 382 | ident, ok := vspec.Type.(*ast.Ident) 383 | if !ok { 384 | continue 385 | } 386 | typ = ident.Name 387 | } 388 | if typ != f.typeName { 389 | // This is not the type we're looking for. 390 | continue 391 | } 392 | // We now have a list of names (from one line of source code) all being 393 | // declared with the desired type. 394 | // Grab their names and actual values and store them in f.values. 395 | for _, name := range vspec.Names { 396 | if name.Name == "_" { 397 | continue 398 | } 399 | // This dance lets the type checker find the values for us. It's a 400 | // bit tricky: look up the object declared by the name, find its 401 | // types.Const, and extract its value. 402 | obj, ok := f.pkg.defs[name] 403 | if !ok { 404 | log.Fatalf("no value for constant %s", name) 405 | } 406 | info := obj.Type().Underlying().(*types.Basic).Info() 407 | if info&types.IsInteger == 0 { 408 | log.Fatalf("can't handle non-integer constant type %s", typ) 409 | } 410 | value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. 411 | if value.Kind() != constant.Int { 412 | log.Fatalf("can't happen: constant is not an integer %s", name) 413 | } 414 | i64, isInt := constant.Int64Val(value) 415 | u64, isUint := constant.Uint64Val(value) 416 | if !isInt && !isUint { 417 | log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) 418 | } 419 | if !isInt { 420 | u64 = uint64(i64) 421 | } 422 | v := Value{ 423 | name: name.Name, 424 | value: u64, 425 | signed: info&types.IsUnsigned == 0, 426 | str: value.String(), 427 | } 428 | f.values = append(f.values, v) 429 | } 430 | } 431 | return false 432 | } 433 | 434 | // Helpers 435 | 436 | // usize returns the number of bits of the smallest unsigned integer 437 | // type that will hold n. Used to create the smallest possible slice of 438 | // integers to use as indexes into the concatenated strings. 439 | func usize(n int) int { 440 | switch { 441 | case n < 1<<8: 442 | return 8 443 | case n < 1<<16: 444 | return 16 445 | default: 446 | // 2^32 is enough constants for anyone. 447 | return 32 448 | } 449 | } 450 | 451 | // buildOneRun generates the variables and String method for a single run of contiguous values. 452 | func (g *Generator) buildOneRun(runs [][]Value, typeName string) { 453 | values := runs[0] 454 | g.Printf("\n") 455 | // g.declareIndexAndNameVar(values, typeName) 456 | // The generated code is simple enough to write as a Printf format. 457 | lessThanZero := "" 458 | if values[0].signed { 459 | lessThanZero = "i < 0 || " 460 | } 461 | if values[0].value == 0 { // Signed or unsigned, 0 is still 0. 462 | g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) 463 | } else { 464 | g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) 465 | } 466 | } 467 | 468 | // Arguments to format are: 469 | // [1]: type name 470 | // [2]: size of index element (8 for uint8 etc.) 471 | // [3]: less than zero check (for signed types) 472 | const stringOneRun = `func (i %[1]s) Valid() bool { 473 | if %[3]si >= %[1]s(len(_%[1]s_index)-1) { 474 | return true 475 | } 476 | return false 477 | } 478 | ` 479 | 480 | // Arguments to format are: 481 | // [1]: type name 482 | // [2]: lowest defined value for type, as a string 483 | // [3]: size of index element (8 for uint8 etc.) 484 | // [4]: less than zero check (for signed types) 485 | /* 486 | */ 487 | const stringOneRunWithOffset = `func (i %[1]s) Valid() bool { 488 | i -= %[2]s 489 | if %[4]si >= %[1]s(len(_%[1]s_index)-1) { 490 | return true 491 | } 492 | return false 493 | } 494 | ` 495 | 496 | // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. 497 | // For this pattern, a single Printf format won't do. 498 | func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { 499 | g.Printf("\n") 500 | // g.declareIndexAndNameVars(runs, typeName) 501 | g.Printf("func (i %s) Valid() bool {\n", typeName) 502 | g.Printf("\tswitch {\n") 503 | for _, values := range runs { 504 | if len(values) == 1 { 505 | g.Printf("\tcase i == %s:\n", &values[0]) 506 | g.Printf("\t\treturn true\n") 507 | continue 508 | } 509 | g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) 510 | if values[0].value != 0 { 511 | g.Printf("\t\ti -= %s\n", &values[0]) 512 | } 513 | g.Printf("\t\treturn true\n") 514 | } 515 | g.Printf("\tdefault:\n") 516 | g.Printf("\t\treturn false\n") 517 | g.Printf("\t}\n") 518 | g.Printf("}\n") 519 | } 520 | 521 | // buildMap handles the case where the space is so sparse a map is a reasonable fallback. 522 | // It's a rare situation but has simple code. 523 | func (g *Generator) buildMap(runs [][]Value, typeName string) { 524 | g.Printf("\n") 525 | g.Printf(stringMap, typeName) 526 | } 527 | 528 | // Argument to format is the type name. 529 | const stringMap = `func (i %[1]s) Valid() bool { 530 | ok := _%[1]s_map[i] 531 | return ok 532 | } 533 | ` 534 | -------------------------------------------------------------------------------- /mediatype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type MediaType"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _MediaType_name_0 = "No tapeLaminated" 9 | _MediaType_name_1 = "Non laminated" 10 | _MediaType_name_2 = "Heat shrink tube" 11 | _MediaType_name_3 = "Invalid tape type" 12 | ) 13 | 14 | var ( 15 | _MediaType_index_0 = [...]uint8{0, 7, 16} 16 | ) 17 | 18 | func (i MediaType) String() string { 19 | switch { 20 | case 0 <= i && i <= 1: 21 | return _MediaType_name_0[_MediaType_index_0[i]:_MediaType_index_0[i+1]] 22 | case i == 3: 23 | return _MediaType_name_1 24 | case i == 17: 25 | return _MediaType_name_2 26 | case i == 255: 27 | return _MediaType_name_3 28 | default: 29 | return "MediaType(" + strconv.FormatInt(int64(i), 10) + ")" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /model_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type Model"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _Model_name_0 = "PT-P750W" 9 | _Model_name_1 = "PT-P710BT" 10 | ) 11 | 12 | func (i Model) String() string { 13 | switch { 14 | case i == 104: 15 | return _Model_name_0 16 | case i == 118: 17 | return _Model_name_1 18 | default: 19 | return "Model(" + strconv.FormatInt(int64(i), 10) + ")" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /notification_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix notification -type Notification"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const _Notification_name = "InvalidCoverOpenCoverClose" 8 | 9 | var _Notification_index = [...]uint8{0, 7, 16, 26} 10 | 11 | func (i Notification) String() string { 12 | if i < 0 || i >= Notification(len(_Notification_index)-1) { 13 | return "Notification(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _Notification_name[_Notification_index[i]:_Notification_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /packbits.go: -------------------------------------------------------------------------------- 1 | package ptouchgo 2 | 3 | import "bytes" 4 | 5 | func packBits(input []byte) ([]byte, error) { 6 | buf := bytes.NewBuffer(make([]byte, 0, 128)) 7 | dst := make([]byte, 0, 1024) 8 | 9 | var rle bool 10 | var repeats int 11 | const maxRepeats = 127 12 | 13 | var finishRaw = func() { 14 | if buf.Len() == 0 { 15 | return 16 | } 17 | dst = append(dst, byte(buf.Len()-1)) 18 | dst = append(dst, buf.Bytes()...) 19 | buf.Reset() 20 | } 21 | 22 | var finishRle = func(b byte, repeats int) { 23 | dst = append(dst, byte(256-(repeats-1))) 24 | dst = append(dst, b) 25 | } 26 | 27 | for i, b := range input { 28 | isLast := i == len(input)-1 29 | if isLast { 30 | if !rle { 31 | buf.WriteByte(b) 32 | finishRaw() 33 | } else { 34 | repeats++ 35 | finishRle(b, repeats) 36 | } 37 | break 38 | } 39 | 40 | if b == input[i+1] { 41 | if !rle { 42 | finishRaw() 43 | rle = true 44 | repeats = 1 45 | } else { 46 | if repeats == maxRepeats { 47 | finishRle(b, repeats) 48 | repeats = 0 49 | } 50 | repeats++ 51 | } 52 | } else { 53 | if !rle { 54 | if buf.Len() == maxRepeats { 55 | finishRaw() 56 | } 57 | buf.WriteByte(b) 58 | } else { 59 | repeats++ 60 | finishRle(b, repeats) 61 | rle = false 62 | repeats = 0 63 | } 64 | } 65 | } 66 | return dst, nil 67 | } 68 | -------------------------------------------------------------------------------- /phasenumber_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix phaseNumber -type PhaseNumber"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _PhaseNumber_name_0 = "EditEditFeed" 9 | _PhaseNumber_name_1 = "NormalCoverOpen" 10 | ) 11 | 12 | var ( 13 | _PhaseNumber_index_0 = [...]uint8{0, 4, 12} 14 | ) 15 | 16 | func (i PhaseNumber) String() string { 17 | switch { 18 | case 0 <= i && i <= 1: 19 | return _PhaseNumber_name_0[_PhaseNumber_index_0[i]:_PhaseNumber_index_0[i+1]] 20 | case i == 20: 21 | return _PhaseNumber_name_1 22 | default: 23 | return "PhaseNumber(" + strconv.FormatInt(int64(i), 10) + ")" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phasetypenumber_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix phaseType -type PhaseTypeNumber"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const _PhaseTypeNumber_name = "EditNormal" 8 | 9 | var _PhaseTypeNumber_index = [...]uint8{0, 4, 10} 10 | 11 | func (i PhaseTypeNumber) String() string { 12 | if i < 0 || i >= PhaseTypeNumber(len(_PhaseTypeNumber_index)-1) { 13 | return "PhaseTypeNumber(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _PhaseTypeNumber_name[_PhaseTypeNumber_index[i]:_PhaseTypeNumber_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /ptouch.go: -------------------------------------------------------------------------------- 1 | // Package ptouchgo is a driver for PT-710BT/PT700/PT750W 2 | package ptouchgo 3 | 4 | import ( 5 | "bytes" 6 | "encoding/hex" 7 | "fmt" 8 | "image" 9 | "image/png" 10 | "io" 11 | "log" 12 | "net/url" 13 | 14 | "github.com/disintegration/imaging" 15 | "github.com/ka2n/ptouchgo/conn" 16 | ) 17 | 18 | const ( 19 | statusOffsetModel = 4 20 | statusOffsetBattery = 6 21 | statusOffsetErrorInfo1 = 8 22 | statusOffsetErrorInfo2 = 9 23 | statusOffsetMediaWidth = 10 24 | statusOffsetMediaType = 11 25 | statusOffsetMode = 15 26 | statusOffsetTapeLength = 17 27 | statusOffsetStatusType = 18 28 | statusOffsetPhaseType = 19 29 | statusOffsetPhaseNumber = 20 30 | statusOffsetNotification = 22 31 | statusOffsetTapeColor = 24 32 | statusOffsetFontColor = 25 33 | statusOffsetHardwareConf = 26 34 | ) 35 | 36 | type Status struct { 37 | Type StatusType 38 | Model Model 39 | Battery BatteryStatusType 40 | Error1 Error1Type 41 | Error2 Error2Type 42 | Mode int 43 | StatusType StatusType 44 | PhaseType PhaseTypeNumber 45 | Phase PhaseNumber 46 | Notification Notification 47 | 48 | MediaType MediaType 49 | TapeColor TapeColor 50 | TapeLength int 51 | TapeWidth TapeWidth 52 | FontColor FontColor 53 | } 54 | 55 | //go:generate stringer -linecomment -type Model 56 | type Model int 57 | 58 | const ( 59 | modelPTP700 Model = 0x67 // PT-P700 60 | modelPTP750W Model = 0x68 // PT-P750W 61 | modelPTP710BT Model = 0x76 // PT-P710BT 62 | ) 63 | 64 | type Error1Type int 65 | 66 | const ( 67 | error1NoMedia Error1Type = 0x01 // No Media 68 | error1CutterJam Error1Type = 0x04 // Cutter Jam 69 | error1WeakBattery Error1Type = 0x08 // Weak battery 70 | error1TooHighVoltageAC Error1Type = 0x06 // Too high voltage from AC 71 | ) 72 | 73 | //go:generate stringer -linecomment -type Error2Type 74 | type Error2Type int 75 | 76 | const ( 77 | error2InvalidMedia Error2Type = 0x01 // Invalid media 78 | error2CoverOpen Error2Type = 0x10 // Cover open 79 | error2Hot Error2Type = 0x20 // Too hot 80 | ) 81 | 82 | //go:generate stringer -linecomment -type TapeWidth 83 | //go:generate go run ./internal/cmd/enum/enum.go -type TapeWidth 84 | type TapeWidth int 85 | 86 | const ( 87 | tapeWidthNone TapeWidth = 0 // No tape 88 | tapeWidth3_5 TapeWidth = 4 // 3.5mm 89 | tapeWidth6 TapeWidth = 6 // 6mm 90 | tapeWidth9 TapeWidth = 9 // 9mm 91 | tapeWidth12 TapeWidth = 12 // 12mm 92 | tapeWidth18 TapeWidth = 18 // 18mm 93 | tapeWidth24 TapeWidth = 24 // 24mm 94 | ) 95 | 96 | //go:generate stringer -linecomment -type MediaType 97 | type MediaType int 98 | 99 | const ( 100 | mediaTypeNone MediaType = 0 // No tape 101 | mediaTypeLaminated MediaType = 0x01 // Laminated 102 | mediaTypeNonLaminated MediaType = 0x03 // Non laminated 103 | mediaTypeHeatShirink MediaType = 0x11 // Heat shrink tube 104 | mediaTypeInvalid MediaType = 0xFF // Invalid tape type 105 | ) 106 | 107 | //go:generate stringer -linecomment -type StatusType 108 | type StatusType int 109 | 110 | const ( 111 | statusTypeReply StatusType = 0 // Reply 112 | statusTypePrintingCompleted StatusType = 0x01 // Printing completed 113 | statusTypeErrorOccured StatusType = 0x02 // Error occured 114 | statusTypeIFModeFinished StatusType = 3 // IFModeFinished(unused) 115 | statusTypePowerOff StatusType = 0x04 // Power off 116 | statusTypeNotification StatusType = 0x05 // Notification 117 | statusTypePhaseChange StatusType = 0x06 // Phase change 118 | ) 119 | 120 | //go:generate stringer -trimprefix phaseType -type PhaseTypeNumber 121 | type PhaseTypeNumber int 122 | 123 | const ( 124 | phaseTypeEdit PhaseTypeNumber = 0x00 // Edit phase 125 | phaseTypeNormal PhaseTypeNumber = 0x01 // Normal phase 126 | ) 127 | 128 | //go:generate stringer -trimprefix phaseNumber -type PhaseNumber 129 | type PhaseNumber int 130 | 131 | const ( 132 | phaseNumberEdit PhaseNumber = 0x00 133 | phaseNumberEditFeed PhaseNumber = 0x01 134 | 135 | phaseNumberNormal PhaseNumber = 0x00 136 | phaseNumberNormalCoverOpen PhaseNumber = 0x14 137 | ) 138 | 139 | //go:generate stringer -trimprefix notification -type Notification 140 | type Notification int 141 | 142 | const ( 143 | notificationInvalid Notification = 0x00 144 | notificationCoverOpen Notification = 0x01 145 | notificationCoverClose Notification = 0x02 146 | ) 147 | 148 | //go:generate stringer -trimprefix tapeColor -type TapeColor 149 | type TapeColor int 150 | 151 | const ( 152 | tapeColorWhite TapeColor = 0x01 153 | tapeColorOther TapeColor = 0x02 154 | tapeColorClear TapeColor = 0x03 155 | tapeColorRed TapeColor = 0x04 156 | tapeColorBlue TapeColor = 0x05 157 | tapeColorYellow TapeColor = 0x06 158 | tapeColorGreen TapeColor = 0x07 159 | tapeColorBlack TapeColor = 0x08 160 | tapeColorClearWhiteText TapeColor = 0x09 161 | tapeColorMatteWhite TapeColor = 0x20 162 | tapeColorMatteClear TapeColor = 0x21 163 | tapeColorMatteSilver TapeColor = 0x22 164 | tapeColorSatinGold TapeColor = 0x23 165 | tapeColorSatinSilver TapeColor = 0x24 166 | tapeColorDBlue TapeColor = 0x30 // TZe-535, TZe-545, TZe-555 167 | tapeColorDRed TapeColor = 0x31 // TZe-435 168 | tapeColorFluorescentOrange TapeColor = 0x40 169 | tapeColorFluorescentyellow TapeColor = 0x41 170 | tapeColorBerryPink TapeColor = 0x50 // TZe-MQP35 171 | tapeColorLightGray TapeColor = 0x51 // TZe-MQL35 172 | tapeColorLimeGreen TapeColor = 0x52 // TZe-MQG35 173 | tapeColorFYellow TapeColor = 0x60 174 | tapeColorFPing TapeColor = 0x61 175 | tapeColorFBlue TapeColor = 0x62 176 | tapeColorHeatShrinkWhite TapeColor = 0x70 177 | tapeColorFlexWhite TapeColor = 0x90 178 | tapeColorFlexYellow TapeColor = 0x91 179 | tapeColorCleaning TapeColor = 0xF0 180 | tapeColorStencil TapeColor = 0xF1 181 | tapeColorInvalid TapeColor = 0xFF 182 | ) 183 | 184 | //go:generate stringer -trimprefix fontColor -type FontColor 185 | type FontColor int 186 | 187 | const ( 188 | fontColorWhite FontColor = 0x01 189 | fontColorRed FontColor = 0x04 190 | fontColorBlue FontColor = 0x05 191 | fontColorBlack FontColor = 0x08 192 | fontColorGold FontColor = 0x0a 193 | fontColorFBlue FontColor = 0x62 194 | fontColorCleaning FontColor = 0xF0 195 | fontColorStencil FontColor = 0xF1 196 | fontColorOther FontColor = 0x02 197 | fontColorInvalid FontColor = 0xFF 198 | ) 199 | 200 | //go:generate stringer -trimprefix battery -type BatteryStatusType 201 | type BatteryStatusType int 202 | 203 | const ( 204 | batteryFull BatteryStatusType = 0 205 | batteryHalf BatteryStatusType = 1 206 | batteryLow BatteryStatusType = 2 207 | batteryChangeBatteries BatteryStatusType = 3 208 | batteryAC BatteryStatusType = 4 209 | ) 210 | 211 | var ( 212 | cmdInitialize = []byte{0x1b, 0x40} 213 | cmdDumpStatus = []byte{0x1b, 0x69, 0x53} 214 | cmdSetRasterMode = []byte{0x1b, 0x69, 0x61, 0x01} // 0: ESC/P, 1: Raster, 3: P-touch Template, but only supported Raster 215 | cmdNotifyModePrefix = []byte{0x1b, 0x69, 0x21} 216 | cmdSetPrintPropertyPrefix = []byte{0x1b, 0x69, 0x7a} 217 | cmdSetPrintModePrefix = []byte{0x1b, 0x69, 0x4d} 218 | cmdSetAutcutPrefix = []byte{0x1b, 0x69, 0x41} // only for PT-P750W 219 | cmdSetExtendedModePrefix = []byte{0x1b, 0x69, 0x4b} 220 | cmdSetFeedAmountPrefix = []byte{0x1b, 0x69, 0x64} 221 | cmdSetCompressionModePrefix = []byte{0x4d} 222 | cmdRasterTransfer = []byte{0x47} 223 | cmdRasterZeroline = []byte{0x5a} 224 | cmdPrint = []byte{0x0c} 225 | cmdPrintAndEject = []byte{0x1a} 226 | ) 227 | 228 | const ( 229 | printPropertyEnableBitMedia = 0x02 230 | printPropertyEnableBitWidth = 0x04 231 | printPropertyEnableBitLength = 0x08 232 | printPropertyEnableBitQuality = 0x40 // unused 233 | printPropertyEnableBitRecoverOnDevice = 0x80 234 | ) 235 | 236 | type Serial struct { 237 | Conn io.ReadWriteCloser 238 | TapeWidthMM uint 239 | Debug bool 240 | } 241 | 242 | // Open connection, address should be a device path string like "/dev/rfcomm0", "usb" or "usb://0x7c35" or "tcp://192.168.100.1:9100") 243 | func Open(address string, TapeWidthMM uint, debug bool) (Serial, error) { 244 | var ser io.ReadWriteCloser 245 | var err error 246 | if address == "usb" { 247 | if debug { 248 | log.Println("Select USB driver with automatic device selection") 249 | } 250 | ser, err = conn.Open("usb", "") 251 | if err != nil { 252 | return Serial{}, err 253 | } 254 | } else { 255 | var driver string 256 | var addr string 257 | u, err := url.Parse(address) 258 | if err != nil { 259 | return Serial{}, err 260 | } 261 | if u.Scheme == "" { 262 | driver = "serial" 263 | addr = u.Path 264 | } else { 265 | driver = u.Scheme 266 | addr = u.Host 267 | } 268 | if debug { 269 | log.Printf("Select %s driver, address: %s\n", driver, addr) 270 | } 271 | 272 | ser, err = conn.Open(driver, addr) 273 | if err != nil { 274 | return Serial{}, err 275 | } 276 | } 277 | return Serial{Conn: ser, TapeWidthMM: TapeWidthMM, Debug: debug}, err 278 | } 279 | 280 | // ClearBuffer clears current state 281 | // If you want to stop ongoing data transfer, 282 | // send ClearBuffer() and Initialize() then printer buffer are cleared and return to data receiving state 283 | func (s Serial) ClearBuffer() error { 284 | // send empty instruction 285 | if s.Debug { 286 | log.Println("ClearBuffer") 287 | } 288 | _, err := s.Conn.Write(make([]byte, 100)) 289 | return err 290 | } 291 | 292 | // Initialize clears mode setting 293 | func (s Serial) Initialize() error { 294 | if s.Debug { 295 | log.Println("Initialize", hex.EncodeToString(cmdInitialize)) 296 | } 297 | _, err := s.Conn.Write(cmdInitialize) 298 | return err 299 | } 300 | 301 | // RequestStatus requests current status 302 | // do not use while printing 303 | func (s Serial) RequestStatus() error { 304 | if s.Debug { 305 | log.Println("RequestStatus", hex.EncodeToString(cmdDumpStatus)) 306 | } 307 | _, err := s.Conn.Write(cmdDumpStatus) 308 | return err 309 | } 310 | 311 | // ReadStatus reads current status from buffer 312 | func (s Serial) ReadStatus() (*Status, error) { 313 | buf := make([]byte, 32) 314 | s.Conn.Read(buf) 315 | return parseStatus(buf) 316 | } 317 | 318 | func (s Serial) SetRasterMode() error { 319 | if s.Debug { 320 | log.Println("SetRasterMode", hex.EncodeToString(cmdSetRasterMode)) 321 | } 322 | _, err := s.Conn.Write(cmdSetRasterMode) 323 | return err 324 | } 325 | 326 | // SetNotificationMode set auto status notification mode 327 | // default: on 328 | func (s Serial) SetNotificationMode(on bool) error { 329 | var b byte 330 | if on { 331 | b = 0x0 332 | } else { 333 | b = 0x1 334 | } 335 | 336 | payload := append(cmdNotifyModePrefix, b) 337 | if s.Debug { 338 | log.Println("SetNotificationMode", on, hex.EncodeToString(payload)) 339 | } 340 | 341 | _, err := s.Conn.Write(payload) 342 | return err 343 | } 344 | 345 | func (s Serial) Close() error { 346 | return s.Conn.Close() 347 | } 348 | 349 | func (s Serial) SetPrintProperty(rasterLines int) error { 350 | var enableFlag int 351 | 352 | enableFlag |= printPropertyEnableBitRecoverOnDevice 353 | 354 | // Tape 355 | tapeWidth := byte(s.TapeWidthMM) 356 | const tapeLength = byte(0x00) 357 | enableFlag |= printPropertyEnableBitWidth 358 | 359 | // Data size 360 | // N4*256*256*256 + N3*256*256 + N2*256 + N1 361 | r := rasterLines 362 | rasterNumN4 := byte(r / (256 * 256 * 256)) 363 | rasterNumN3 := byte(r % (256 * 256 * 256) / (256 * 256)) 364 | rasterNumN2 := byte(r % (256 * 256 * 256) % (256 * 256) / 256) 365 | rasterNumN1 := byte(r % 256) 366 | 367 | // Media type 368 | const mediaType = byte(0x00) 369 | 370 | const page = byte(0x00) // firstPage: 0, otherPage: 1 371 | 372 | const eeprom = byte(0x00) 373 | 374 | data := append(cmdSetPrintPropertyPrefix, []byte{ 375 | byte(enableFlag), 376 | mediaType, 377 | tapeWidth, 378 | tapeLength, 379 | rasterNumN1, 380 | rasterNumN2, 381 | rasterNumN3, 382 | rasterNumN4, 383 | page, 384 | eeprom, 385 | }...) 386 | 387 | if s.Debug { 388 | log.Println("SetPrintProperty", hex.EncodeToString(data)) 389 | } 390 | 391 | _, err := s.Conn.Write(data) 392 | return err 393 | } 394 | 395 | func (s Serial) SetPrintMode(autocut, mirror bool) error { 396 | var v int 397 | if autocut { 398 | v = setBit(v, 6) 399 | } 400 | if mirror { 401 | v = setBit(v, 7) 402 | } 403 | 404 | payload := append(cmdSetPrintModePrefix, byte(v)) 405 | if s.Debug { 406 | log.Println("SetPrintMode", hex.EncodeToString(payload)) 407 | } 408 | 409 | _, err := s.Conn.Write(payload) 410 | return err 411 | } 412 | 413 | func (s Serial) SetExtendedMode(pt750halfcut bool, noChainprint bool, specialTapeDisableCut bool, highDPI bool, noClearBuffer bool) error { 414 | var v int 415 | if pt750halfcut { 416 | v = setBit(v, 2) 417 | } 418 | 419 | if noChainprint { 420 | v = setBit(v, 3) 421 | } 422 | 423 | if specialTapeDisableCut { 424 | v = setBit(v, 4) 425 | } 426 | 427 | if highDPI { 428 | v = setBit(v, 6) 429 | } 430 | 431 | if noClearBuffer { 432 | v = setBit(v, 7) 433 | } 434 | 435 | payload := append(cmdSetExtendedModePrefix, byte(v)) 436 | if s.Debug { 437 | log.Println("SetExtendedMode", hex.EncodeToString(payload)) 438 | } 439 | 440 | _, err := s.Conn.Write(payload) 441 | return err 442 | } 443 | 444 | func (s Serial) SetFeedAmount(amount int) error { 445 | n1 := byte(amount % 256) 446 | n2 := byte(amount / 256) 447 | 448 | payload := append(cmdSetFeedAmountPrefix, []byte{ 449 | n1, n2, 450 | }...) 451 | if s.Debug { 452 | log.Println("SetFeedAmount", hex.EncodeToString(payload)) 453 | } 454 | _, err := s.Conn.Write(payload) 455 | return err 456 | } 457 | 458 | func (s Serial) SetAutocutPerPagesForPTP750W(pages int) error { 459 | if pages == 0 { 460 | pages = 1 461 | } 462 | payload := append(cmdSetAutcutPrefix, byte(pages)) 463 | if s.Debug { 464 | log.Println("SetAutocutPerPagesForPTP750W", hex.EncodeToString(payload)) 465 | } 466 | _, err := s.Conn.Write(payload) 467 | return err 468 | } 469 | 470 | func (s Serial) SetCompressionModeEnabled(enabled bool) error { 471 | var v byte 472 | if enabled { 473 | v = 0x02 474 | } 475 | 476 | payload := append(cmdSetCompressionModePrefix, v) 477 | if s.Debug { 478 | log.Println("SetCompressionModeEnabled", hex.EncodeToString(payload)) 479 | } 480 | _, err := s.Conn.Write(payload) 481 | return err 482 | } 483 | 484 | func (s Serial) SendImage(tiffdata []byte) error { 485 | if s.Debug { 486 | log.Println("SendImage", len(tiffdata)) 487 | } 488 | _, err := s.Conn.Write(tiffdata) 489 | return err 490 | } 491 | 492 | func (s Serial) Print() error { 493 | if s.Debug { 494 | log.Printf("Print %08b", cmdPrint) 495 | } 496 | _, err := s.Conn.Write(cmdPrint) 497 | return err 498 | } 499 | 500 | func (s Serial) PrintAndEject() error { 501 | if s.Debug { 502 | log.Printf("PrintAndEject %08b", cmdPrintAndEject) 503 | } 504 | _, err := s.Conn.Write(cmdPrintAndEject) 505 | return err 506 | } 507 | 508 | func (s Serial) Reset() error { 509 | err := s.ClearBuffer() 510 | if err != nil { 511 | return err 512 | } 513 | return s.Initialize() 514 | } 515 | 516 | func LoadPNGImage(r io.Reader, tapeWidth TapeWidth) ([]byte, int, error) { 517 | p, err := png.Decode(r) 518 | if err != nil { 519 | return nil, 0, err 520 | } 521 | return LoadRawImage(p, tapeWidth) 522 | } 523 | 524 | func LoadRawImage(p image.Image, tapeWidth TapeWidth) ([]byte, int, error) { 525 | ws := 128 526 | var canvas image.Image 527 | 528 | size := p.Bounds().Size() 529 | if size.X == ws { 530 | canvas = imaging.FlipH(p) 531 | } else if size.Y == ws { 532 | canvas = imaging.Transpose(p) 533 | } else { 534 | return nil, 0, fmt.Errorf("image size must have %dpx width or height for %d tape, got: %dx%d", ws, tapeWidth, size.X, size.Y) 535 | } 536 | 537 | size = canvas.Bounds().Size() 538 | bytesWidth := size.X / 8 539 | if size.X%8 != 0 { 540 | bytesWidth++ 541 | } 542 | 543 | data := make([]byte, bytesWidth*size.Y) 544 | 545 | // 1bit 546 | for y := 0; y < size.Y; y++ { 547 | for x := 0; x < size.X; x++ { 548 | r, g, b, _ := canvas.At(x, y).RGBA() 549 | lightness := float64(55*r+182*g+18*b) / float64(0xffff*(55+182+18)) 550 | if lightness <= 0.5 { 551 | data[y*bytesWidth+x/8] |= 0x80 >> uint(x%8) 552 | } 553 | } 554 | } 555 | 556 | return data, bytesWidth, nil 557 | } 558 | 559 | func CompressImage(data []byte, bytesWidth int) ([]byte, error) { 560 | var dataBuf bytes.Buffer 561 | max := len(data) 562 | 563 | for i := 0; i < max; i += bytesWidth { 564 | to := i + bytesWidth 565 | if to > max { 566 | to = max 567 | } 568 | chunk := data[i:to] 569 | 570 | packed, err := packBits(chunk) 571 | if err != nil { 572 | return nil, err 573 | } 574 | 575 | length := len(packed) 576 | 577 | dataBuf.Write(cmdRasterTransfer) 578 | dataBuf.Write([]byte{ 579 | byte(uint(length % 256)), 580 | byte(uint(length / 256)), 581 | }) 582 | dataBuf.Write(packed) 583 | } 584 | 585 | return dataBuf.Bytes(), nil 586 | } 587 | 588 | func parseStatus(in []byte) (*Status, error) { 589 | if len(in) != 32 { 590 | return nil, fmt.Errorf("status must be 32 bytes, got: %d", len(in)) 591 | } 592 | 593 | return &Status{ 594 | Type: StatusType(in[statusOffsetStatusType]), 595 | Model: Model(in[statusOffsetModel]), 596 | Battery: BatteryStatusType(in[statusOffsetBattery]), 597 | Error1: Error1Type(in[statusOffsetErrorInfo1]), 598 | Error2: Error2Type(in[statusOffsetErrorInfo2]), 599 | Mode: int(in[statusOffsetMode]), 600 | StatusType: StatusType(in[statusOffsetStatusType]), 601 | PhaseType: PhaseTypeNumber(in[statusOffsetPhaseType]), 602 | Phase: PhaseNumber(in[statusOffsetPhaseNumber]), 603 | Notification: Notification(in[statusOffsetNotification]), 604 | MediaType: MediaType(in[statusOffsetMediaType]), 605 | TapeColor: TapeColor(in[statusOffsetTapeColor]), 606 | TapeLength: int(in[statusOffsetTapeLength]), 607 | TapeWidth: TapeWidth(in[statusOffsetMediaWidth]), 608 | FontColor: FontColor(in[statusOffsetFontColor]), 609 | }, nil 610 | } 611 | 612 | func setBit(n int, pos uint) int { 613 | n |= (1 << pos) 614 | return n 615 | } 616 | 617 | func clearBit(n int, pos uint) int { 618 | mask := ^(1 << pos) 619 | n &= mask 620 | return n 621 | } 622 | -------------------------------------------------------------------------------- /statustype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type StatusType"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const _StatusType_name = "ReplyPrinting completedError occuredIFModeFinished(unused)Power offNotificationPhase change" 8 | 9 | var _StatusType_index = [...]uint8{0, 5, 23, 36, 58, 67, 79, 91} 10 | 11 | func (i StatusType) String() string { 12 | if i < 0 || i >= StatusType(len(_StatusType_index)-1) { 13 | return "StatusType(" + strconv.FormatInt(int64(i), 10) + ")" 14 | } 15 | return _StatusType_name[_StatusType_index[i]:_StatusType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /tapecolor_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -trimprefix tapeColor -type TapeColor"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _TapeColor_name_0 = "WhiteOtherClearRedBlueYellowGreenBlackClearWhiteText" 9 | _TapeColor_name_1 = "MatteWhiteMatteClearMatteSilverSatinGoldSatinSilver" 10 | _TapeColor_name_2 = "DBlueDRed" 11 | _TapeColor_name_3 = "FluorescentOrangeFluorescentyellow" 12 | _TapeColor_name_4 = "BerryPinkLightGrayLimeGreen" 13 | _TapeColor_name_5 = "FYellowFPingFBlue" 14 | _TapeColor_name_6 = "HeatShrinkWhite" 15 | _TapeColor_name_7 = "FlexWhiteFlexYellow" 16 | _TapeColor_name_8 = "CleaningStencil" 17 | _TapeColor_name_9 = "Invalid" 18 | ) 19 | 20 | var ( 21 | _TapeColor_index_0 = [...]uint8{0, 5, 10, 15, 18, 22, 28, 33, 38, 52} 22 | _TapeColor_index_1 = [...]uint8{0, 10, 20, 31, 40, 51} 23 | _TapeColor_index_2 = [...]uint8{0, 5, 9} 24 | _TapeColor_index_3 = [...]uint8{0, 17, 34} 25 | _TapeColor_index_4 = [...]uint8{0, 9, 18, 27} 26 | _TapeColor_index_5 = [...]uint8{0, 7, 12, 17} 27 | _TapeColor_index_7 = [...]uint8{0, 9, 19} 28 | _TapeColor_index_8 = [...]uint8{0, 8, 15} 29 | ) 30 | 31 | func (i TapeColor) String() string { 32 | switch { 33 | case 1 <= i && i <= 9: 34 | i -= 1 35 | return _TapeColor_name_0[_TapeColor_index_0[i]:_TapeColor_index_0[i+1]] 36 | case 32 <= i && i <= 36: 37 | i -= 32 38 | return _TapeColor_name_1[_TapeColor_index_1[i]:_TapeColor_index_1[i+1]] 39 | case 48 <= i && i <= 49: 40 | i -= 48 41 | return _TapeColor_name_2[_TapeColor_index_2[i]:_TapeColor_index_2[i+1]] 42 | case 64 <= i && i <= 65: 43 | i -= 64 44 | return _TapeColor_name_3[_TapeColor_index_3[i]:_TapeColor_index_3[i+1]] 45 | case 80 <= i && i <= 82: 46 | i -= 80 47 | return _TapeColor_name_4[_TapeColor_index_4[i]:_TapeColor_index_4[i+1]] 48 | case 96 <= i && i <= 98: 49 | i -= 96 50 | return _TapeColor_name_5[_TapeColor_index_5[i]:_TapeColor_index_5[i+1]] 51 | case i == 112: 52 | return _TapeColor_name_6 53 | case 144 <= i && i <= 145: 54 | i -= 144 55 | return _TapeColor_name_7[_TapeColor_index_7[i]:_TapeColor_index_7[i+1]] 56 | case 240 <= i && i <= 241: 57 | i -= 240 58 | return _TapeColor_name_8[_TapeColor_index_8[i]:_TapeColor_index_8[i+1]] 59 | case i == 255: 60 | return _TapeColor_name_9 61 | default: 62 | return "TapeColor(" + strconv.FormatInt(int64(i), 10) + ")" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tapewidth_enum.go: -------------------------------------------------------------------------------- 1 | // Code generated by "goenum -type TapeWidth"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | func (i TapeWidth) Valid() bool { 6 | switch { 7 | case i == 0: 8 | return true 9 | case i == 4: 10 | return true 11 | case i == 6: 12 | return true 13 | case i == 9: 14 | return true 15 | case i == 12: 16 | return true 17 | case i == 18: 18 | return true 19 | case i == 24: 20 | return true 21 | default: 22 | return false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tapewidth_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -linecomment -type TapeWidth"; DO NOT EDIT. 2 | 3 | package ptouchgo 4 | 5 | import "strconv" 6 | 7 | const ( 8 | _TapeWidth_name_0 = "No tape" 9 | _TapeWidth_name_1 = "3.5mm" 10 | _TapeWidth_name_2 = "6mm" 11 | _TapeWidth_name_3 = "9mm" 12 | _TapeWidth_name_4 = "12mm" 13 | _TapeWidth_name_5 = "18mm" 14 | _TapeWidth_name_6 = "24mm" 15 | ) 16 | 17 | func (i TapeWidth) String() string { 18 | switch { 19 | case i == 0: 20 | return _TapeWidth_name_0 21 | case i == 4: 22 | return _TapeWidth_name_1 23 | case i == 6: 24 | return _TapeWidth_name_2 25 | case i == 9: 26 | return _TapeWidth_name_3 27 | case i == 12: 28 | return _TapeWidth_name_4 29 | case i == 18: 30 | return _TapeWidth_name_5 31 | case i == 24: 32 | return _TapeWidth_name_6 33 | default: 34 | return "TapeWidth(" + strconv.FormatInt(int64(i), 10) + ")" 35 | } 36 | } 37 | --------------------------------------------------------------------------------