]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/truetypefs.c
add (unfinished but working) truetypefs
[plan9front.git] / sys / src / cmd / truetypefs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include <9p.h>
6 #include <bio.h>
7 #include <ttf.h>
8
9 static char Egreg[] = "my memory of truetype is fading";
10 static char Enoent[] = "not found";
11
12 enum { MAXSUB = 0x100 };
13
14 typedef struct TFont TFont;
15 typedef struct TSubfont TSubfont;
16
17 struct TFont {
18         int ref;
19         Qid qid;
20         Qid fileqid;
21         char *fontfile;
22         int nfontfile;
23         TTFont *ttf;
24         char *name9p;
25         char *name;
26         int size;
27         TFont *next, *prev;
28         TSubfont *sub[256];
29 };
30
31 struct TSubfont {
32         TFont *font;
33         Rune start, end;
34         Qid qid;
35         char *data;
36         int ndata;
37         TSubfont *next;
38 };
39
40 typedef struct FidAux FidAux;
41
42 struct FidAux {
43         enum {
44                 FIDROOT,
45                 FIDFONT,
46                 FIDFONTF,
47                 FIDSUB,
48         } type;
49         TFont *f;
50         TSubfont *sub;
51 };
52
53 TFont fontl = {.next = &fontl, .prev = &fontl};
54
55 static void *
56 emalloc(ulong n)
57 {
58         void *v;
59         
60         v = mallocz(n, 1);
61         if(v == nil) sysfatal("malloc: %r");
62         setmalloctag(v, getcallerpc(&n));
63         return v;
64 }
65
66 static uvlong
67 qidgen(void)
68 {
69         static uvlong x;
70         
71         return ++x;
72 }
73
74 static void
75 fsattach(Req *r)
76 {
77         r->ofcall.qid = (Qid){0, 0, QTDIR};
78         r->fid->qid = r->ofcall.qid;
79         r->fid->aux = emalloc(sizeof(FidAux));
80         respond(r, nil);
81 }
82
83 void
84 mksubfonts(TFont *f)
85 {
86         int k;
87         TTChMap *c;
88         TTFontU *u;
89         TSubfont *s;
90         Fmt fmt;
91         int got0;
92         
93         u = f->ttf->u;
94         fmtstrinit(&fmt);
95         fmtprint(&fmt, "%d\t%d\n", f->ttf->ascentpx + f->ttf->descentpx, f->ttf->ascentpx);
96         got0 = 0;
97         for(c = u->cmap; c < u->cmap + u->ncmap; c++){
98                 for(k = c->start; k < c->end; k += MAXSUB){
99                         s = emalloc(sizeof(TSubfont));
100                         s->start = c->start;
101                         if(c->start == 0) got0 = 1;
102                         s->end = k + MAXSUB - 1;
103                         if(s->end > c->end)
104                                 s->end = c->end;
105                         s->font = f;
106                         s->qid = (Qid){qidgen(), 0, 0};
107                         s->next = f->sub[c->start >> 8 & 0xff];
108                         f->sub[c->start >> 8 & 0xff] = s;
109                         fmtprint(&fmt, "%#.4ux\t%#.4ux\ts.%.4ux-%.4ux\n", s->start, s->end, s->start, s->end);
110                 }
111         }
112         if(!got0){
113                 s = emalloc(sizeof(TSubfont));
114                 s->start = 0;
115                 s->end = 0;
116                 s->font = f;
117                 s->qid = (Qid){qidgen(), 0, 0};
118                 s->next = f->sub[0];
119                 f->sub[0] = s;
120                 fmtprint(&fmt, "%#.4ux\t%#.4ux\ts.%.4ux-%.4ux\n", 0, 0, 0, 0);
121         }
122         f->fontfile = fmtstrflush(&fmt);
123         f->nfontfile = strlen(f->fontfile);
124 }
125
126 static void
127 blit(uchar *t, int x, int y, int tstride, uchar *s, int w, int h)
128 {
129         int tx, ty, sx, sy;
130         u16int b;
131         uchar *tp, *sp;
132         
133         if(y < 0) y = 0;
134         ty = y;
135         sp = s;
136         for(sy = 0; sy < h; sy++, ty++){
137                 tx = x;
138                 tp = t + ty * tstride + (tx >> 3);
139                 b = 0;
140                 for(sx = 0; sx < w; sx += 8){
141                         b |= *sp++ << 8 - (tx & 7);
142                         *tp++ |= b >> 8;
143                         b <<= 8;
144                 }
145                 *tp |= b >> 8;
146         }
147 }
148
149 static void
150 compilesub(TFont *f, TSubfont *s)
151 {
152         int n, i, w, x, h, g, sz;
153         char *d, *p;
154         TTGlyph **gs;
155         TTFont *t;
156         
157         t = f->ttf;
158         n = s->end - s->start + 1;
159         gs = emalloc9p(sizeof(TTGlyph *) * n);
160         w = 0;
161         h = t->ascentpx + t->descentpx;
162         for(i = 0; i < n; i++){
163                 if(s->start + i == 0)
164                         g = 0;
165                 else
166                         g = ttffindchar(t, s->start + i);
167                 gs[i] = ttfgetglyph(t, g, 1);
168                 w += gs[i]->width;
169         }
170         sz = 5 * 12 + (w+7>>3) * h + 3 * 12 + (n + 1) * 6;
171         d = emalloc(sz);
172         p = d + sprint(d, "%11s %11d %11d %11d %11d ", "k1", 0, 0, w, h);
173         x = 0;
174         for(i = 0; i < n; i++){
175                 blit((uchar*)p, x, t->ascentpx - gs[i]->ymaxpx, w+7>>3, gs[i]->bit, gs[i]->width, gs[i]->height);
176                 x += gs[i]->width;
177         }
178         p += (w+7>>3) * h;
179         p += sprint(p, "%11d %11d %11d ", n, h, t->ascentpx);
180         x = 0;
181         for(i = 0; i < n; i++){
182                 *p++ = x;
183                 *p++ = x >> 8;
184                 *p++ = 0;
185                 *p++ = h;
186                 *p++ = gs[i]->xminpx;
187                 *p++ = gs[i]->advanceWidthpx;
188                 x += gs[i]->width;
189         }
190         *p++ = x;
191         *p = x >> 8;
192         s->data = d;
193         s->ndata = sz;
194         for(i = 0; i < n; i++)
195                 ttfputglyph(gs[i]);
196         free(gs);
197 }
198
199
200 TFont *
201 tryfont(char *name)
202 {
203         TTFont *ttf;
204         TFont *f;
205         char *d, *buf, *p;
206         int sz;
207         
208         for(f = fontl.next; f != &fontl; f = f->next)
209                 if(strcmp(f->name9p, name) == 0)
210                         return f;
211         d = strrchr(name, '.');
212         if(d == nil){
213         inval:
214                 werrstr("invalid file name");
215                 return nil;
216         }
217         sz = strtol(d + 1, &p, 10);
218         if(d[1] == 0 || *p != 0)
219                 goto inval;
220         buf = estrdup9p(name);
221         buf[d - name] = 0;
222         ttf = ttfopen(buf, sz, 0);
223         if(ttf == nil){
224                 free(buf);
225                 return nil;
226         }
227         f = emalloc(sizeof(TFont));
228         f->ttf = ttf;
229         f->name9p = strdup(name);
230         f->name = buf;
231         f->size = sz;
232         f->qid = (Qid){qidgen(), 0, QTDIR};
233         f->fileqid = (Qid){qidgen(), 0, 0};
234         f->next = &fontl;
235         f->prev = fontl.prev;
236         f->next->prev = f;
237         f->prev->next = f;
238         mksubfonts(f);
239         return f;
240 }
241
242 static char *
243 fsclone(Fid *old, Fid *new)
244 {
245         new->aux = emalloc(sizeof(FidAux));
246         *(FidAux*)new->aux = *(FidAux*)old->aux;
247         return nil;
248 }
249
250 static void
251 fsdestroyfid(Fid *f)
252 {
253         FidAux *fa;
254         
255         fa = f->aux;
256         free(fa);
257         f->aux = nil;
258 }
259
260 static TSubfont *
261 findsubfont(TFont *f, char *name)
262 {
263         char *p, *q;
264         char buf[16];
265         int a, b;
266         TSubfont *s;
267
268         if(name[0] != 's' || name[1] != '.' || name[2] == '-')
269                 return nil;
270         a = strtol(name + 2, &p, 16);
271         if(*p != '-')
272                 return nil;
273         b = strtol(p + 1, &q, 16);
274         if(p + 1 == q || *q != 0)
275                 return nil;
276         snprint(buf, nelem(buf), "s.%.4ux-%.4ux", a, b);
277         if(strcmp(buf, name) != 0)
278                 return nil;
279         for(s = f->sub[a>>8&0xff]; s != nil; s = s->next)
280                 if(s->start == a && s->end == b)
281                         break;
282         return s;
283 }
284
285 static char *
286 fswalk(Fid *fid, char *name, Qid *qid)
287 {
288         static char errbuf[ERRMAX];
289         FidAux *fa;
290
291         fa = fid->aux;
292         assert(fa != nil);
293         switch(fa->type){
294         case FIDROOT:
295                 fa->f = tryfont(name);
296                 if(fa->f == nil){
297                         rerrstr(errbuf, nelem(errbuf));
298                         return errbuf;
299                 }
300                 fa->f->ref++;
301                 fa->type = FIDFONT;
302                 fid->qid = fa->f->qid;
303                 *qid = fa->f->qid;
304                 return nil;
305         case FIDFONT:
306                 if(strcmp(name, "font") == 0){
307                         fa->type = FIDFONTF;
308                         fid->qid = fa->f->fileqid;
309                         *qid = fa->f->fileqid;
310                         return nil;
311                 }
312                 fa->sub = findsubfont(fa->f, name);
313                 if(fa->sub == nil)
314                         return Enoent;
315                 fa->type = FIDSUB;
316                 fid->qid = fa->sub->qid;
317                 *qid = fa->sub->qid;
318                 return nil;
319         default:
320                 return Egreg;
321         }
322 }
323
324 static void
325 fsstat(Req *r)
326 {
327         FidAux *fa;
328
329         fa = r->fid->aux;
330         assert(fa != nil);
331         r->d.uid = estrdup9p(getuser());
332         r->d.gid = estrdup9p(getuser());
333         r->d.muid = estrdup9p(getuser());
334         r->d.mtime = r->d.atime = time(0);
335         r->d.qid = r->fid->qid;
336         switch(fa->type){
337         case FIDROOT:
338                 r->d.mode = 0777;
339                 r->d.name = estrdup9p("/");
340                 respond(r, nil);
341                 break;
342         case FIDFONT:
343                 r->d.mode = 0777;
344                 r->d.name = estrdup9p(fa->f->name9p);
345                 respond(r, nil);
346                 break;
347         case FIDFONTF:
348                 r->d.mode = 0666;
349                 r->d.name = estrdup9p("font");
350                 r->d.length = fa->f->nfontfile;
351                 respond(r, nil);
352                 break;
353         case FIDSUB:
354                 r->d.mode = 0666;
355                 r->d.name = smprint("s.%.4ux-%.4ux", fa->sub->start, fa->sub->end);
356                 r->d.length = fa->sub->ndata;
357                 respond(r, nil);
358                 break;
359         default:
360                 respond(r, Egreg);
361         }
362 }
363
364 static int
365 fontdirread(int n, Dir *d, void *aux)
366 {
367         FidAux *fa;
368         
369         fa = aux;
370         if(n == 0){
371                 d->name = estrdup9p("font");
372                 d->uid = estrdup9p(getuser());
373                 d->gid = estrdup9p(getuser());
374                 d->muid = estrdup9p(getuser());
375                 d->mode = 0666;
376                 d->qid = fa->f->fileqid;
377                 d->mtime = d->atime = time(0);
378                 d->length = fa->f->nfontfile;
379                 return 0;
380         }
381         return -1;
382 }
383
384 static void
385 fsread(Req *r)
386 {
387         FidAux *fa;
388
389         fa = r->fid->aux;
390         assert(fa != nil);
391         switch(fa->type){
392         case FIDROOT:
393                 respond(r, nil);
394                 break;
395         case FIDFONT:
396                 dirread9p(r, fontdirread, fa);
397                 respond(r, nil);
398                 break;
399         case FIDFONTF:
400                 readbuf(r, fa->f->fontfile, fa->f->nfontfile);
401                 respond(r, nil);
402                 break;
403         case FIDSUB:
404                 if(fa->sub->data == nil)
405                         compilesub(fa->f, fa->sub);
406                 readbuf(r, fa->sub->data, fa->sub->ndata);
407                 respond(r, nil);
408                 break;
409         default:
410                 respond(r, Egreg);
411         }
412 }
413
414 Srv fssrv = {
415         .attach = fsattach,
416         .walk1 = fswalk,
417         .clone = fsclone,
418         .stat = fsstat,
419         .read = fsread,
420         .destroyfid = fsdestroyfid,
421 };
422
423 void
424 main(int argc, char **argv)
425 {
426         ARGBEGIN {
427         default:
428                 sysfatal("usage");
429         } ARGEND;
430         
431         unmount(nil, "/n/ttf");
432         postmountsrv(&fssrv, nil, "/n/ttf", 0);
433         exits(nil);
434 }