9fans.net/go@v0.0.5/draw/openfont.go (about) 1 package draw 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "os" 9 "os/exec" 10 "strings" 11 ) 12 13 func parsefontscale(name string) (scale int, fname string) { 14 i := 0 15 scale = 0 16 for i < len(name) && '0' <= name[i] && name[i] <= '9' { 17 scale = scale*10 + int(name[i]) - '0' 18 i++ 19 } 20 if i < len(name) && name[i] == '*' && scale > 0 { 21 return scale, name[i+1:] 22 } 23 return 1, name 24 } 25 26 // OpenFont reads the named file and returns the font it defines. 27 // The name may be an absolute path, or it may identify a file 28 // in a standard font directory: 29 // /lib/font/bit, /usr/local/plan9, /mnt/font, etc. 30 // 31 // In contrast to Plan 9, but matching Plan 9 from User Space, 32 // font names are a small language describing the desired font. 33 // See the package documentation for details. 34 func (d *Display) OpenFont(name string) (*Font, error) { 35 // nil display is allowed, for querying font metrics 36 // in non-draw program. 37 if d != nil { 38 d.mu.Lock() 39 defer d.mu.Unlock() 40 } 41 return d.openFont(name) 42 } 43 44 func (d *Display) openFont1(name string) (*Font, error) { 45 scale, fname := parsefontscale(name) 46 47 data, err := ioutil.ReadFile(fname) 48 49 if err != nil && strings.HasPrefix(fname, "/lib/font/bit/") { 50 root := os.Getenv("PLAN9") 51 if root == "" { 52 root = "/usr/local/plan9" 53 } 54 name1 := root + "/font/" + fname[len("/lib/font/bit/"):] 55 data1, err1 := ioutil.ReadFile(name1) 56 fname, data, err = name1, data1, err1 57 if scale > 1 { 58 name = fmt.Sprintf("%d*%s", scale, fname) 59 } else { 60 name = fname 61 } 62 } 63 64 if err != nil && strings.HasPrefix(fname, "/mnt/font/") { 65 data1, err1 := fontPipe(fname[len("/mnt/font/"):]) 66 if err1 == nil { 67 data, err = data1, err1 68 } 69 } 70 if err != nil { 71 return nil, err 72 } 73 74 f, err := d.buildFont(data, name) 75 if err != nil { 76 return nil, err 77 } 78 79 if scale != 1 { 80 f.Scale = scale 81 f.Height *= scale 82 f.Ascent *= scale 83 f.width *= scale 84 } 85 return f, nil 86 } 87 88 func swapfont(targ *Font, oldp, newp **Font) { 89 if targ != *oldp { 90 log.Fatalf("bad swapfont %p %p %p", targ, *oldp, *newp) 91 } 92 93 old := *oldp 94 new := *newp 95 var tmp Font 96 copyfont(&tmp, old) 97 copyfont(old, new) 98 copyfont(new, &tmp) 99 100 *oldp = new 101 *newp = old 102 } 103 104 func copyfont(dst, src *Font) { 105 dst.Display = src.Display 106 dst.Name = src.Name 107 dst.Height = src.Height 108 dst.Ascent = src.Ascent 109 dst.Scale = src.Scale 110 dst.width = src.width 111 dst.age = src.age 112 dst.maxdepth = src.maxdepth 113 dst.cache = src.cache 114 dst.subf = src.subf 115 dst.sub = src.sub 116 dst.cacheimage = src.cacheimage 117 } 118 119 func hidpiname(f *Font) string { 120 // If font name has form x,y return y. 121 i := strings.Index(f.namespec, ",") 122 if i >= 0 { 123 return f.namespec[i+1:] 124 } 125 126 // If font name is /mnt/font/Name/Size/font, scale Size. 127 if strings.HasPrefix(f.Name, "/mnt/font/") { 128 i := strings.Index(f.Name[len("/mnt/font/"):], "/") 129 if i < 0 { 130 goto Scale 131 } 132 i += len("/mnt/font/") + 1 133 if i >= len(f.Name) || f.Name[i] < '0' || '9' < f.Name[i] { 134 goto Scale 135 } 136 j := i 137 size := 0 138 for j < len(f.Name) && '0' <= f.Name[j] && f.Name[j] <= '9' { 139 size = size*10 + int(f.Name[j]) - '0' 140 j++ 141 } 142 return fmt.Sprintf("%s%d%s", f.Name[:i], size*2, f.Name[j:]) 143 } 144 145 // Otherwise use pixel doubling. 146 Scale: 147 return fmt.Sprintf("%d*%s", f.Scale*2, f.Name) 148 } 149 150 func loadhidpi(f *Font) { 151 if f.hidpi == f { 152 return 153 } 154 if f.hidpi != nil { 155 swapfont(f, &f.lodpi, &f.hidpi) 156 return 157 } 158 159 name := hidpiname(f) 160 fnew, err := f.Display.openFont1(name) 161 if err != nil { 162 return 163 } 164 f.hidpi = fnew 165 swapfont(f, &f.lodpi, &f.hidpi) 166 } 167 168 func (d *Display) openFont(name string) (*Font, error) { 169 // If font name has form x,y use x for lodpi, y for hidpi 170 namespec := name 171 if i := strings.Index(name, ","); i >= 0 { 172 name = name[:i] 173 } 174 175 f, err := d.openFont1(name) 176 if err != nil { 177 return nil, err 178 } 179 f.lodpi = f 180 f.namespec = namespec 181 182 // add to display list for when dpi changes. 183 // d can be nil when invoked from mc. 184 if d != nil { 185 f.ondisplaylist = true 186 f.prev = d.lastfont 187 f.next = nil 188 if f.prev != nil { 189 f.prev.next = f 190 } else { 191 d.firstfont = f 192 } 193 d.lastfont = f 194 195 // if this is a hi-dpi display, find hi-dpi version and swap 196 if d.HiDPI() { 197 loadhidpi(f) 198 } 199 } 200 201 return f, nil 202 } 203 204 func fontPipe(name string) ([]byte, error) { 205 data, err := exec.Command("fontsrv", "-pp", name).CombinedOutput() 206 207 // Success marked with leading \001. Otherwise an error happened. 208 if len(data) > 0 && data[0] != '\001' { 209 i := bytes.IndexByte(data, '\n') 210 if i >= 0 { 211 data = data[:i] 212 } 213 return nil, fmt.Errorf("fontsrv -pp %s: %v", name, data) 214 } 215 if err != nil { 216 return nil, err 217 } 218 return data[1:], nil 219 }