]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libttf/head.c
libaml: fix gc bug, need to amltake()/amldrop() temporary buffer
[plan9front.git] / sys / src / libttf / head.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <ctype.h>
6 #include <ttf.h>
7 #include "impl.h"
8
9 void
10 ttfunpack(TTFontU *f, char *p, ...)
11 {
12         va_list va;
13         int n;
14         uchar *p1;
15         u16int *p2;
16         u32int *p4;
17         
18         va_start(va, p);
19         for(; *p != 0; p++)
20                 switch(*p){
21                 case 'b':
22                         p1 = va_arg(va, u8int *);
23                         *p1 = Bgetc(f->bin);
24                         break;
25                 case 'B':
26                         p4 = va_arg(va, u32int *);
27                         *p4 = Bgetc(f->bin);
28                         break;
29                 case 'w':
30                         p2 = va_arg(va, u16int *);
31                         *p2 = Bgetc(f->bin) << 8;
32                         *p2 |= Bgetc(f->bin);
33                         break;
34                 case 'W':
35                         p4 = va_arg(va, u32int *);
36                         *p4 = Bgetc(f->bin) << 8;
37                         *p4 |= Bgetc(f->bin);
38                         break;
39                 case 'S':
40                         p4 = va_arg(va, u32int *);
41                         *p4 = (char)Bgetc(f->bin) << 8;
42                         *p4 |= Bgetc(f->bin);
43                         break;
44                 case 'l':
45                         p4 = va_arg(va, u32int *);
46                         *p4 = Bgetc(f->bin) << 24;
47                         *p4 |= Bgetc(f->bin) << 16;
48                         *p4 |= Bgetc(f->bin) << 8;
49                         *p4 |= Bgetc(f->bin);
50                         break;
51                 case '.': Bgetc(f->bin); break;
52                 case ' ': break;
53                 default:
54                         if(isdigit(*p)){
55                                 n = strtol(p, &p, 10);
56                                 p--;
57                                 Bseek(f->bin, n, 1);
58                         }else abort();
59                         break;
60                 }
61 }
62
63 static int
64 directory(TTFontU *f)
65 {
66         u32int scaler;
67         int i;
68
69         ttfunpack(f, "lw .. .. ..", &scaler, &f->ntab);
70         if(scaler != 0x74727565 && scaler != 0x10000){
71                 werrstr("unknown scaler type %#ux", scaler);
72                 return -1;
73         }
74         f->tab = mallocz(sizeof(TTTable) * f->ntab, 1);
75         if(f->tab == nil) return -1;
76         for(i = 0; i < f->ntab; i++)
77                 ttfunpack(f, "llll", &f->tab[i].tag, &f->tab[i].csum, &f->tab[i].offset, &f->tab[i].len);
78         return 0;
79 }
80
81 int
82 ttfgototable(TTFontU *f, char *str)
83 {
84         TTTable *t;
85         u32int tag;
86         
87         tag = (u8int)str[0] << 24 | (u8int)str[1] << 16 | (u8int)str[2] << 8 | (u8int)str[3];
88         for(t = f->tab; t < f->tab + f->ntab; t++)
89                 if(t->tag == tag){
90                         Bseek(f->bin, t->offset, 0);
91                         return t->len;
92                 }
93         werrstr("no such table '%s'", str);
94         return -1;
95 }
96
97 static int
98 ttfparseloca(TTFontU *f)
99 {
100         int len, i;
101         u32int x;
102         
103         len = ttfgototable(f, "loca");
104         if(len < 0) return -1;
105         x = 0;
106         if(f->longloca){
107                 if(len > (f->numGlyphs + 1) * 4) len = (f->numGlyphs + 1) * 4;
108                 for(i = 0; i < len/4; i++){
109                         x = Bgetc(f->bin) << 24;
110                         x |= Bgetc(f->bin) << 16;
111                         x |= Bgetc(f->bin) << 8;
112                         x |= Bgetc(f->bin);
113                         f->ginfo[i].loca = x;
114                 }
115         }else{
116                 if(len > (f->numGlyphs + 1) * 2) len = (f->numGlyphs + 1) * 2;
117                 for(i = 0; i < len/2; i++){
118                         x = Bgetc(f->bin) << 8;
119                         x |= Bgetc(f->bin);
120                         f->ginfo[i].loca = x * 2;
121                 }
122         }
123         for(; i < f->numGlyphs; i++)
124                 f->ginfo[i].loca = x;
125         return 0;
126 }
127
128 static int
129 ttfparsehmtx(TTFontU *f)
130 {
131         int i;
132         u16int x, y;
133         int len;
134         int maxlsb;
135         
136         len = ttfgototable(f, "hmtx");
137         if(len < 0)
138                 return -1;
139         if(f->numOfLongHorMetrics > f->numGlyphs){
140                 werrstr("nonsensical header: numOfLongHorMetrics > numGlyphs");
141                 return -1;
142         }
143         for(i = 0; i < f->numOfLongHorMetrics; i++){
144                 ttfunpack(f, "ww", &x, &y);
145                 f->ginfo[i].advanceWidth = x;
146                 f->ginfo[i].lsb = y;
147         }
148         maxlsb = (len - 2 * f->numOfLongHorMetrics) / 2;
149         if(maxlsb > f->numGlyphs){
150                 werrstr("nonsensical header: maxlsb > f->numGlyphs");
151                 return -1;
152         }
153         for(; i < maxlsb; i++){
154                 ttfunpack(f, "w", &y);
155                 f->ginfo[i].advanceWidth = x;
156                 f->ginfo[i].lsb = y;
157         }
158         for(; i < f->numGlyphs; i++){
159                 f->ginfo[i].advanceWidth = x;
160                 f->ginfo[i].lsb = y;
161         }
162         return 0;
163 }
164
165 int
166 ttfparsecvt(TTFontU *f)
167 {
168         int len;
169         int i;
170         int x;
171         u8int *p;
172         short *w;
173
174         len = ttfgototable(f, "cvt ");
175         if(len <= 0) return 0;
176         f->cvtu = mallocz(len, 1);
177         if(f->cvtu == 0) return -1;
178         Bread(f->bin, f->cvtu, len);
179         p = (u8int *) f->cvtu;
180         f->ncvtu = len / 2;
181         w = f->cvtu;
182         for(i = 0; i < f->ncvtu; i++){
183                 x = (short)(p[0] << 8 | p[1]);
184                 p += 2;
185                 *w++ = x;
186         }
187         return 0;
188 }
189
190 static int
191 ttfparseos2(TTFontU *f)
192 {
193         int len;
194         u16int usWinAscent, usWinDescent;
195         
196         len = ttfgototable(f, "OS/2 ");
197         if(len < 0)
198                 return -1;
199         if(len < 78){
200                 werrstr("OS/2 table too short");
201                 return -1;
202         }
203         ttfunpack(f, "68 6 ww", &usWinAscent, &usWinDescent);
204         f->ascent = usWinAscent;
205         f->descent = usWinDescent;
206         return 0;
207 }
208
209 static void
210 ttfcloseu(TTFontU *u)
211 {
212         int i;
213
214         if(u == nil) return;
215         Bterm(u->bin);
216         for(i = 0; i < u->ncmap; i++)
217                 free(u->cmap[i].tab);
218         free(u->cmap);
219         free(u->ginfo);
220         free(u->tab);
221         free(u->cvtu);
222         free(u);
223 }
224
225 void
226 ttfclose(TTFont *f)
227 {
228         int i;
229
230         if(f == nil) return;
231         if(--f->u->ref <= 0)
232                 ttfcloseu(f->u);
233         for(i = 0; i < f->u->maxFunctionDefs; i++)
234                 free(f->func[i].pgm);
235         free(f->hintstack);
236         free(f->func);
237         free(f->storage);
238         free(f->twilight);
239         free(f->twiorg);
240         free(f->cvt);
241         free(f);
242 }
243
244 static TTFont *
245 ttfscaleu(TTFontU *u, int ppem)
246 {
247         TTFont *f;
248         int i;
249         
250         f = mallocz(sizeof(TTFont), 1);
251         if(f == nil) return nil;
252         f->u = u;
253         u->ref++;
254         f->ppem = ppem;
255         f->ncvt = u->ncvtu;
256         f->cvt = malloc(sizeof(int) * u->ncvtu);
257         if(f->cvt == nil) goto error;
258         for(i = 0; i < u->ncvtu; i++)
259                 f->cvt[i] = ttfrounddiv(u->cvtu[i] * ppem * 64, u->emsize);
260         f->hintstack = mallocz(sizeof(u32int) * u->maxStackElements, 1);
261         f->func = mallocz(sizeof(TTFunction) * u->maxFunctionDefs, 1);
262         f->storage = mallocz(sizeof(u32int) * u->maxStorage, 1);
263         f->twilight = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);
264         f->twiorg = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);
265         if(f->hintstack == nil || f->func == nil || f->storage == nil || f->twilight == nil || f->twiorg == nil) goto error;
266         f->ascentpx = (u->ascent * ppem + u->emsize - 1) / (u->emsize);
267         f->descentpx = (u->descent * ppem + u->emsize - 1) / (u->emsize);
268         if(ttfrunfpgm(f) < 0) goto error;
269         if(ttfruncvt(f) < 0) goto error;
270         return f;
271
272 error:
273         ttfclose(f);
274         return nil;
275 }
276
277 TTFont *
278 ttfopen(char *name, int ppem, int)
279 {
280         Biobuf *b;
281         TTFontU *u;
282         
283         if(ppem < 0){
284                 werrstr("invalid ppem argument");
285                 return nil;
286         }
287         b = Bopen(name, OREAD);
288         if(b == nil)
289                 return nil;
290         u = mallocz(sizeof(TTFontU), 1);
291         if(u == nil)
292                 return nil;
293         u->bin = b;
294         u->nkern = -1;
295         if(directory(u) < 0) goto error;
296         if(ttfgototable(u, "head") < 0) goto error;
297         ttfunpack(u, "16 w W 16 wwww 6 w", &u->flags, &u->emsize, &u->xmin, &u->ymin, &u->xmax, &u->ymax, &u->longloca);
298         if(ttfgototable(u, "maxp") < 0) goto error;
299         ttfunpack(u, "4 wwwwwwwwwwwwww",
300                 &u->numGlyphs, &u->maxPoints, &u->maxCountours, &u->maxComponentPoints, &u->maxComponentCountours,
301                 &u->maxZones, &u->maxTwilightPoints, &u->maxStorage, &u->maxFunctionDefs, &u->maxInstructionDefs,
302                 &u->maxStackElements, &u->maxSizeOfInstructions, &u->maxComponentElements, &u->maxComponentDepth);
303         u->ginfo = mallocz(sizeof(TTGlyphInfo) * (u->numGlyphs + 1), 1);
304         if(u->ginfo == nil)
305                 goto error;
306         if(ttfgototable(u, "hhea") < 0) goto error;
307         ttfunpack(u, "10 wwww 16 w", &u->advanceWidthMax, &u->minLeftSideBearing, &u->minRightSideBearing, &u->xMaxExtent, &u->numOfLongHorMetrics);
308         if(ttfparseloca(u) < 0) goto error;
309         if(ttfparsehmtx(u) < 0) goto error;
310         if(ttfparsecvt(u) < 0) goto error;
311         if(ttfparsecmap(u) < 0) goto error;
312         if(ttfparseos2(u) < 0) goto error;
313         return ttfscaleu(u, ppem);
314
315 error:
316         ttfcloseu(u);
317         return nil;
318 }
319
320 TTFont *
321 ttfscale(TTFont *f, int ppem, int)
322 {
323         return ttfscaleu(f->u, ppem);
324 }
325
326 int
327 ttfrounddiv(int a, int b)
328 {
329         if(b < 0){ a = -a; b = -b; }
330         if(a > 0)
331                 return (a + b/2) / b;
332         else
333                 return (a - b/2) / b;
334 }
335
336 int
337 ttfvrounddiv(vlong a, int b)
338 {
339         if(b < 0){ a = -a; b = -b; }
340         if(a > 0)
341                 return (a + b/2) / b;
342         else
343                 return (a - b/2) / b;
344 }