9fans.net/go@v0.0.5/draw/buildfont.go (about) 1 package draw 2 3 import ( 4 "fmt" 5 "strconv" 6 "unicode" 7 ) 8 9 func skip(b []byte) []byte { 10 for len(b) > 0 && (b[0] == ' ' || b[0] == '\t' || b[0] == '\n') { 11 b = b[1:] 12 } 13 return b 14 } 15 16 func strtol(b []byte) (int, []byte) { 17 b = skip(b) 18 i := 0 19 if len(b) == 0 || b[0] < '0' || '9' < b[0] { 20 return 0, b 21 } 22 for i < len(b) && '0' <= b[i] && b[i] <= '9' || 'A' <= b[i] && b[i] <= 'Z' || 'a' <= b[i] && b[i] <= 'z' { 23 i++ 24 } 25 n, _ := strconv.ParseInt(string(b[:i]), 0, 0) 26 return int(n), skip(b[i:]) 27 } 28 29 // BuildFont builds a font of the given name using the description 30 // provided by the buffer, typically the contents of a font file. 31 func (d *Display) BuildFont(buf []byte, name string) (*Font, error) { 32 d.mu.Lock() 33 defer d.mu.Unlock() 34 return d.buildFont(buf, name) 35 } 36 37 func (d *Display) buildFont(buf []byte, name string) (*Font, error) { 38 fnt := &Font{ 39 Display: d, 40 Scale: 1, 41 Name: name, 42 cache: make([]cacheinfo, _NFCACHE+_NFLOOK), 43 subf: make([]cachesubf, _NFSUBF), 44 age: 1, 45 } 46 s := buf 47 fnt.Height, s = strtol(s) 48 fnt.Ascent, s = strtol(s) 49 if fnt.Height <= 0 || fnt.Ascent <= 0 { 50 return nil, fmt.Errorf("bad height or ascent in font file") 51 } 52 for { 53 if len(s) == 0 || s[0] < '0' || '9' < s[0] { 54 goto Errbad 55 } 56 var min, max int 57 min, s = strtol(s) 58 if len(s) == 0 || s[0] < '0' || '9' < s[0] { 59 goto Errbad 60 } 61 max, s = strtol(s) 62 if len(s) == 0 || min > unicode.MaxRune || max > unicode.MaxRune || min > max { 63 return nil, fmt.Errorf("illegal subfont range") 64 } 65 offset, t := strtol(s) 66 if len(t) < len(s) { 67 s = t 68 } 69 c := &cachefont{ 70 min: rune(min), 71 max: rune(max), 72 offset: offset, 73 } 74 t = s 75 for len(s) > 0 && s[0] != ' ' && s[0] != '\n' && s[0] != '\t' { 76 s = s[1:] 77 } 78 c.name = string(t[:len(t)-len(s)]) 79 fnt.sub = append(fnt.sub, c) 80 s = skip(s) 81 if len(s) == 0 { 82 break 83 } 84 } 85 return fnt, nil 86 87 Errbad: 88 return nil, fmt.Errorf("bad font format: number expected (char position %d)", len(buf)-len(s)) 89 } 90 91 // Free frees the server resources for the Font. Fonts have a finalizer that 92 // calls Free automatically, if necessary, for garbage collected Images, but it 93 // is more efficient to be explicit. 94 // TODO: Implement the Finalizer! 95 func (f *Font) Free() { 96 if f == nil { 97 return 98 } 99 f.lock() 100 defer f.unlock() 101 102 if f.ondisplaylist { 103 f.ondisplaylist = false 104 if f.next != nil { 105 f.next.prev = f.prev 106 } else { 107 f.Display.lastfont = f.prev 108 } 109 if f.prev != nil { 110 f.prev.next = f.next 111 } else { 112 f.Display.firstfont = f.next 113 } 114 } 115 116 if f.lodpi != f { 117 f.lodpi.Free() 118 } 119 if f.hidpi != f { 120 f.hidpi.Free() 121 } 122 123 f.free() 124 } 125 126 func (f *Font) free() { 127 if f == nil { 128 return 129 } 130 for _, subf := range f.subf { 131 s := subf.f 132 if s != nil && (f.Display == nil || s != f.Display.defaultSubfont) { 133 s.free() 134 } 135 } 136 f.cacheimage.free() 137 }