font: unmap glyphs with .fmap
[neatroff.git] / font.c
blobdfc6845cf596b56b4c28c6b35116095c34b8af65
1 /* font handling */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "roff.h"
7 /* convert wid in device unitwidth size to size sz */
8 #define DEVWID(sz, wid) (((wid) * (sz) + (dev_uwid / 2)) / dev_uwid)
10 /* flags for gpat->flg */
11 #define GF_PAT 1 /* gsub/gpos pattern glyph */
12 #define GF_REP 2 /* gsub replacement glyph */
13 #define GF_CON 4 /* context glyph */
14 #define GF_GRP 8 /* glyph group */
16 /* glyph substitution and positioning rules */
17 struct grule {
18 struct gpat { /* rule description */
19 short g; /* glyph index */
20 short flg; /* pattern flags; GF_* */
21 short x, y, xadv, yadv; /* gpos data */
22 } *pats; /* rule pattern */
23 short len; /* pats[] length */
24 short feat, scrp; /* rule's feature and script */
27 struct font {
28 char name[FNLEN];
29 char fontname[FNLEN];
30 int spacewid;
31 int special;
32 int cs, cs_ps, bd, zoom; /* for .cs, .bd, .fzoom requests */
33 int s1, n1, s2, n2; /* for .tkf request */
34 struct glyph *gl; /* glyphs present in the font */
35 int gl_n, gl_sz; /* number of glyphs in the font */
36 struct dict *gl_dict; /* mapping from gl[i].id to i */
37 struct dict *ch_dict; /* charset mapping */
38 struct dict *ch_map; /* characters mapped via font_map() */
39 /* font features and scripts */
40 char feat_name[NFEATS][8]; /* feature names */
41 int feat_set[NFEATS]; /* feature enabled */
42 char scrp_name[NSCRPS][8]; /* script names */
43 int scrp; /* current script */
44 /* glyph substitution and positioning */
45 struct grule *gsub; /* glyph substitution rules */
46 int gsub_n, gsub_sz;
47 struct grule *gpos; /* glyph positioning rules */
48 int gpos_n, gpos_sz;
49 struct iset *gsub0; /* rules matching a glyph at pos 0 */
50 struct iset *gpos0; /* rules matching a glyph at pos 0 */
51 struct iset *ggrp; /* glyph groups */
54 /* find a glyph by its name */
55 struct glyph *font_find(struct font *fn, char *name)
57 int i = dict_get(fn->ch_map, name);
58 if (i == -1) /* -2 means the glyph has been unmapped */
59 i = dict_get(fn->ch_dict, name);
60 return i >= 0 ? fn->gl + i : NULL;
63 /* find a glyph by its device-dependent identifier */
64 struct glyph *font_glyph(struct font *fn, char *id)
66 int i = dict_get(fn->gl_dict, id);
67 return i >= 0 ? &fn->gl[i] : NULL;
70 static int font_glyphput(struct font *fn, char *id, char *name, int type)
72 struct glyph *g;
73 if (fn->gl_n == fn->gl_sz) {
74 fn->gl_sz = fn->gl_sz + 1024;
75 fn->gl = mextend(fn->gl, fn->gl_n, fn->gl_sz, sizeof(fn->gl[0]));
77 g = &fn->gl[fn->gl_n];
78 snprintf(g->id, sizeof(g->id), "%s", id);
79 snprintf(g->name, sizeof(g->name), "%s", name);
80 g->type = type;
81 g->font = fn;
82 dict_put(fn->gl_dict, g->id, fn->gl_n);
83 return fn->gl_n++;
86 /* map character name to the given glyph; remove the mapping if id is NULL */
87 int font_map(struct font *fn, char *name, char *id)
89 int gidx = -1;
90 if (id)
91 gidx = font_glyph(fn, id) ? font_glyph(fn, id) - fn->gl : -2;
92 dict_put(fn->ch_map, name, gidx);
93 return 0;
96 /* return nonzero if character name has been mapped with font_map() */
97 int font_mapped(struct font *fn, char *name)
99 return dict_get(fn->ch_map, name) != -1;
102 /* enable/disable ligatures; first bit for liga and the second bit for rlig */
103 static int font_featlg(struct font *fn, int val)
105 int ret = 0;
106 ret |= font_feat(fn, "liga", val & 1);
107 ret |= font_feat(fn, "rlig", val & 2) << 1;
108 return ret;
111 /* enable/disable pairwise kerning */
112 static int font_featkn(struct font *fn, int val)
114 return font_feat(fn, "kern", val);
117 /* glyph index in fn->glyphs[] */
118 static int font_idx(struct font *fn, struct glyph *g)
120 return g ? g - fn->gl : -1;
123 static int font_gpatmatch(struct font *fn, struct gpat *p, int g)
125 int *r;
126 if (!(p->flg & GF_GRP))
127 return p->g == g;
128 r = iset_get(fn->ggrp, p->g);
129 while (r && *r >= 0)
130 if (*r++ == g)
131 return 1;
132 return 0;
135 static int font_rulematch(struct font *fn, struct grule *rule,
136 int *src, int slen, int *dst, int dlen)
138 int sidx = 0; /* the index of matched glyphs in src */
139 int ncon = 0; /* number of initial context glyphs */
140 struct gpat *pats = rule->pats;
141 int j;
142 if (fn->scrp >= 0 && fn->scrp != rule->scrp)
143 return 0;
144 if (!fn->feat_set[rule->feat])
145 return 0;
146 /* the number of initial context glyphs */
147 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
148 ncon++;
149 if (dlen < ncon)
150 return 0;
151 /* matching the base pattern */
152 for (; j < rule->len; j++) {
153 if (pats[j].flg & GF_REP)
154 continue;
155 if (sidx >= slen || !font_gpatmatch(fn, &pats[j], src[sidx]))
156 return 0;
157 sidx++;
159 /* matching the initial context */
160 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
161 if (!font_gpatmatch(fn, &pats[j], dst[j - ncon]))
162 return 0;
163 return 1;
166 /* find a matching gsub/gpos rule; *idx should be -1 initially */
167 static int font_findrule(struct font *fn, int gsub, int pos,
168 int *fwd, int fwdlen, int *ctx, int ctxlen, int *idx)
170 struct grule *rules = gsub ? fn->gsub : fn->gpos;
171 int *r1 = iset_get(gsub ? fn->gsub0 : fn->gpos0, fwd[0]);
172 while (r1 && r1[++*idx] >= 0) {
173 if (r1[*idx] >= pos && font_rulematch(fn, &rules[r1[*idx]],
174 fwd, fwdlen, ctx, ctxlen))
175 return r1[*idx];
177 return -1;
180 /* perform all possible gpos rules on src */
181 static void font_performgpos(struct font *fn, int *src, int slen,
182 int *x, int *y, int *xadv, int *yadv)
184 struct grule *gpos = fn->gpos;
185 struct gpat *pats;
186 int i, k;
187 for (i = 0; i < slen; i++) {
188 int idx = -1;
189 while (1) {
190 int r = font_findrule(fn, 0, 0, src + i, slen - i,
191 src + i, i, &idx);
192 if (r < 0)
193 break;
194 pats = gpos[r].pats;
195 /* we should accumulate the values... */
196 for (k = 0; k < gpos[r].len; k++) {
197 x[i + k] += pats[k].x;
198 y[i + k] += pats[k].y;
199 xadv[i + k] += pats[k].xadv;
200 yadv[i + k] += pats[k].yadv;
206 /* find the first gsub rule after pos that matches any glyph in src */
207 static int font_firstgsub(struct font *fn, int pos, int *src, int slen)
209 int best = -1;
210 int i;
211 for (i = 0; i < slen; i++) {
212 int idx = -1;
213 int r = font_findrule(fn, 1, pos, src + i, slen - i,
214 src + i, i, &idx);
215 if (r >= 0 && (best < 0 || r < best))
216 best = r;
218 return best;
221 /* apply the given gsub rule to all matches in src */
222 static int font_gsubapply(struct font *fn, struct grule *rule,
223 int *src, int slen, int *smap)
225 int dst[WORDLEN];
226 int dlen = 0;
227 int dmap[WORDLEN];
228 int i, j;
229 memset(dmap, 0, slen * sizeof(dmap[i]));
230 for (i = 0; i < slen; i++) {
231 dmap[dlen] = smap[i];
232 if (font_rulematch(fn, rule, src + i, slen - i,
233 dst + dlen, dlen)) {
234 for (j = 0; j < rule->len; j++) {
235 if (rule->pats[j].flg & GF_REP)
236 dst[dlen++] = rule->pats[j].g;
237 if (rule->pats[j].flg & GF_PAT)
238 i++;
240 i--;
241 } else {
242 dst[dlen++] = src[i];
245 memcpy(src, dst, dlen * sizeof(dst[0]));
246 memcpy(smap, dmap, dlen * sizeof(dmap[0]));
247 return dlen;
250 /* perform all possible gsub rules on src */
251 static int font_performgsub(struct font *fn, int *src, int slen, int *smap)
253 int i = -1;
254 while (++i >= 0) {
255 if ((i = font_firstgsub(fn, i, src, slen)) < 0)
256 break;
257 slen = font_gsubapply(fn, &fn->gsub[i], src, slen, smap);
259 return slen;
262 int font_layout(struct font *fn, struct glyph **gsrc, int nsrc, int sz,
263 struct glyph **gdst, int *dmap,
264 int *x, int *y, int *xadv, int *yadv, int lg, int kn)
266 int dst[WORDLEN];
267 int ndst = nsrc;
268 int i;
269 int featlg, featkn;
270 /* initialising dst */
271 for (i = 0; i < nsrc; i++)
272 dst[i] = font_idx(fn, gsrc[i]);
273 for (i = 0; i < ndst; i++)
274 dmap[i] = i;
275 memset(x, 0, ndst * sizeof(x[0]));
276 memset(y, 0, ndst * sizeof(y[0]));
277 memset(xadv, 0, ndst * sizeof(xadv[0]));
278 memset(yadv, 0, ndst * sizeof(yadv[0]));
279 /* substitution rules */
280 if (lg)
281 featlg = font_featlg(fn, 3);
282 ndst = font_performgsub(fn, dst, ndst, dmap);
283 if (lg)
284 font_featlg(fn, featlg);
285 /* positioning rules */
286 if (kn)
287 featkn = font_featkn(fn, 1);
288 font_performgpos(fn, dst, ndst, x, y, xadv, yadv);
289 if (kn)
290 font_featkn(fn, featkn);
291 for (i = 0; i < ndst; i++)
292 gdst[i] = fn->gl + dst[i];
293 return ndst;
296 static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid)
298 struct glyph *g;
299 char tok[ILNLEN];
300 char name[ILNLEN];
301 char id[ILNLEN];
302 int type;
303 if (fscanf(fin, "%s %s", name, tok) != 2)
304 return 1;
305 if (!strcmp("---", name))
306 sprintf(name, "c%04d", *n);
307 if (strcmp("\"", tok)) {
308 if (fscanf(fin, "%d %s", &type, id) != 2)
309 return 1;
310 *gid = dict_get(fn->gl_dict, id);
311 if (*gid < 0) {
312 *gid = font_glyphput(fn, id, name, type);
313 g = &fn->gl[*gid];
314 sscanf(tok, "%hd,%hd,%hd,%hd,%hd", &g->wid,
315 &g->llx, &g->lly, &g->urx, &g->ury);
317 dict_put(fn->ch_dict, name, *gid);
318 (*n)++;
319 } else {
320 dict_put(fn->ch_map, name, *gid);
322 return 0;
325 static int font_findfeat(struct font *fn, char *feat)
327 int i;
328 for (i = 0; i < LEN(fn->feat_name) && fn->feat_name[i][0]; i++)
329 if (!strcmp(feat, fn->feat_name[i]))
330 return i;
331 if (i < LEN(fn->feat_name)) {
332 snprintf(fn->feat_name[i], sizeof(fn->feat_name[i]), "%s", feat);
333 return i;
335 return -1;
338 static int font_findscrp(struct font *fn, char *scrp)
340 int i;
341 for (i = 0; i < LEN(fn->scrp_name) && fn->scrp_name[i][0]; i++)
342 if (!strcmp(scrp, fn->scrp_name[i]))
343 return i;
344 if (i < LEN(fn->scrp_name)) {
345 snprintf(fn->scrp_name[i], sizeof(fn->scrp_name[i]), "%s", scrp);
346 return i;
348 return -1;
351 static struct gpat *font_gpat(struct font *fn, int len)
353 struct gpat *pats = xmalloc(len * sizeof(pats[0]));
354 memset(pats, 0, len * sizeof(pats[0]));
355 return pats;
358 static struct grule *font_gsub(struct font *fn, int len, int feat, int scrp)
360 struct grule *rule;
361 struct gpat *pats = font_gpat(fn, len);
362 if (fn->gsub_n == fn->gsub_sz) {
363 fn->gsub_sz = fn->gsub_sz + 1024;
364 fn->gsub = mextend(fn->gsub, fn->gsub_n, fn->gsub_sz,
365 sizeof(fn->gsub[0]));
367 rule = &fn->gsub[fn->gsub_n++];
368 rule->pats = pats;
369 rule->len = len;
370 rule->feat = feat;
371 rule->scrp = scrp;
372 return rule;
375 static struct grule *font_gpos(struct font *fn, int len, int feat, int scrp)
377 struct grule *rule;
378 struct gpat *pats = font_gpat(fn, len);
379 if (fn->gpos_n == fn->gpos_sz) {
380 fn->gpos_sz = fn->gpos_sz + 1024;
381 fn->gpos = mextend(fn->gpos, fn->gpos_n, fn->gpos_sz,
382 sizeof(fn->gpos[0]));
384 rule = &fn->gpos[fn->gpos_n++];
385 rule->pats = pats;
386 rule->len = len;
387 rule->feat = feat;
388 rule->scrp = scrp;
389 return rule;
392 static int font_readgpat(struct font *fn, struct gpat *p, char *s)
394 if (s[0] == '@') {
395 p->g = atoi(s + 1);
396 if (iset_len(fn->ggrp, p->g) == 1)
397 p->g = iset_get(fn->ggrp, p->g)[0];
398 else
399 p->flg |= GF_GRP;
400 } else {
401 p->g = font_idx(fn, font_glyph(fn, s));
403 return p->g < 0;
406 static void font_readfeat(struct font *fn, char *tok, int *feat, int *scrp)
408 *scrp = -1;
409 if (strchr(tok, ':')) { /* "feature:script" */
410 *scrp = font_findscrp(fn, strchr(tok, ':') + 1);
411 strchr(tok, ':')[0] = '\0';
413 *feat = font_findfeat(fn, tok);
416 static int font_readgsub(struct font *fn, FILE *fin)
418 char tok[128];
419 struct grule *rule;
420 int feat, scrp;
421 int i, n;
422 if (fscanf(fin, "%s %d", tok, &n) != 2)
423 return 1;
424 font_readfeat(fn, tok, &feat, &scrp);
425 rule = font_gsub(fn, n, feat, scrp);
426 for (i = 0; i < n; i++) {
427 if (fscanf(fin, "%s", tok) != 1)
428 return 1;
429 if (tok[0] == '-')
430 rule->pats[i].flg = GF_PAT;
431 if (tok[0] == '=')
432 rule->pats[i].flg = GF_CON;
433 if (tok[0] == '+')
434 rule->pats[i].flg = GF_REP;
435 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok + 1))
436 return 0;
438 return 0;
441 static int font_readgpos(struct font *fn, FILE *fin)
443 char tok[128];
444 char *col;
445 struct grule *rule;
446 int feat, scrp;
447 int i, n;
448 if (fscanf(fin, "%s %d", tok, &n) != 2)
449 return 1;
450 font_readfeat(fn, tok, &feat, &scrp);
451 rule = font_gpos(fn, n, feat, scrp);
452 for (i = 0; i < n; i++) {
453 if (fscanf(fin, "%s", tok) != 1)
454 return 1;
455 col = strchr(tok, ':');
456 if (col)
457 *col = '\0';
458 rule->pats[i].flg = GF_PAT;
459 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok))
460 return 0;
461 if (col)
462 sscanf(col + 1, "%hd%hd%hd%hd",
463 &rule->pats[i].x, &rule->pats[i].y,
464 &rule->pats[i].xadv, &rule->pats[i].yadv);
466 return 0;
469 static int font_readggrp(struct font *fn, FILE *fin)
471 char tok[ILNLEN];
472 int id, n, i, g;
473 if (fscanf(fin, "%d %d", &id, &n) != 2)
474 return 1;
475 for (i = 0; i < n; i++) {
476 if (fscanf(fin, "%s", tok) != 1)
477 return 1;
478 g = font_idx(fn, font_glyph(fn, tok));
479 if (g >= 0)
480 iset_put(fn->ggrp, id, g);
482 return 0;
485 static int font_readkern(struct font *fn, FILE *fin)
487 char c1[ILNLEN], c2[ILNLEN];
488 struct grule *rule;
489 int val;
490 if (fscanf(fin, "%s %s %d", c1, c2, &val) != 3)
491 return 1;
492 rule = font_gpos(fn, 2, font_findfeat(fn, "kern"), -1);
493 rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
494 rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
495 rule->pats[0].xadv = val;
496 rule->pats[0].flg = GF_PAT;
497 rule->pats[1].flg = GF_PAT;
498 return 0;
501 static void font_lig(struct font *fn, char *lig)
503 char c[GNLEN];
504 int g[WORDLEN];
505 struct grule *rule;
506 char *s = lig;
507 int j, n = 0;
508 while (utf8read(&s, c) > 0)
509 g[n++] = font_idx(fn, font_find(fn, c));
510 rule = font_gsub(fn, n + 1, font_findfeat(fn, "liga"), -1);
511 for (j = 0; j < n; j++) {
512 rule->pats[j].g = g[j];
513 rule->pats[j].flg = GF_PAT;
515 rule->pats[n].g = font_idx(fn, font_find(fn, lig));
516 rule->pats[n].flg = GF_REP;
519 static void skipline(FILE* filp)
521 int c;
522 do {
523 c = getc(filp);
524 } while (c != '\n' && c != EOF);
527 static struct gpat *font_rulefirstpat(struct font *fn, struct grule *rule)
529 int i;
530 for (i = 0; i < rule->len; i++)
531 if (!(rule->pats[i].flg & (GF_REP | GF_CON)))
532 return &rule->pats[i];
533 return NULL;
536 static void font_isetinsert(struct font *fn, struct iset *iset, int rule, struct gpat *p)
538 if (p->flg & GF_GRP) {
539 int *r = iset_get(fn->ggrp, p->g);
540 while (r && *r >= 0)
541 iset_put(iset, *r++, rule);
542 } else {
543 if (p->g >= 0)
544 iset_put(iset, p->g, rule);
548 struct font *font_open(char *path)
550 struct font *fn;
551 int ch_g = -1; /* last glyph in the charset */
552 int ch_n = 0; /* number of glyphs in the charset */
553 char tok[ILNLEN];
554 FILE *fin;
555 char ligs[512][GNLEN];
556 int ligs_n = 0;
557 int i;
558 fin = fopen(path, "r");
559 if (!fin)
560 return NULL;
561 fn = xmalloc(sizeof(*fn));
562 if (!fn) {
563 fclose(fin);
564 return NULL;
566 memset(fn, 0, sizeof(*fn));
567 fn->gl_dict = dict_make(-1, 1, 0);
568 fn->ch_dict = dict_make(-1, 1, 0);
569 fn->ch_map = dict_make(-1, 1, 0);
570 fn->ggrp = iset_make();
571 while (fscanf(fin, "%s", tok) == 1) {
572 if (!strcmp("char", tok)) {
573 font_readchar(fn, fin, &ch_n, &ch_g);
574 } else if (!strcmp("kern", tok)) {
575 font_readkern(fn, fin);
576 } else if (!strcmp("ligatures", tok)) {
577 while (fscanf(fin, "%s", ligs[ligs_n]) == 1) {
578 if (!strcmp("0", ligs[ligs_n]))
579 break;
580 if (ligs_n < LEN(ligs))
581 ligs_n++;
583 } else if (!strcmp("gsub", tok)) {
584 font_readgsub(fn, fin);
585 } else if (!strcmp("gpos", tok)) {
586 font_readgpos(fn, fin);
587 } else if (!strcmp("ggrp", tok)) {
588 font_readggrp(fn, fin);
589 } else if (!strcmp("spacewidth", tok)) {
590 fscanf(fin, "%d", &fn->spacewid);
591 } else if (!strcmp("special", tok)) {
592 fn->special = 1;
593 } else if (!strcmp("name", tok)) {
594 fscanf(fin, "%s", fn->name);
595 } else if (!strcmp("fontname", tok)) {
596 fscanf(fin, "%s", fn->fontname);
597 } else if (!strcmp("charset", tok)) {
598 while (!font_readchar(fn, fin, &ch_n, &ch_g))
600 break;
602 skipline(fin);
604 for (i = 0; i < ligs_n; i++)
605 font_lig(fn, ligs[i]);
606 fclose(fin);
607 fn->gsub0 = iset_make();
608 fn->gpos0 = iset_make();
609 for (i = 0; i < fn->gsub_n; i++)
610 font_isetinsert(fn, fn->gsub0, i,
611 font_rulefirstpat(fn, &fn->gsub[i]));
612 for (i = 0; i < fn->gpos_n; i++)
613 font_isetinsert(fn, fn->gpos0, i,
614 font_rulefirstpat(fn, &fn->gpos[i]));
615 fn->scrp = -1;
616 return fn;
619 void font_close(struct font *fn)
621 int i;
622 for (i = 0; i < fn->gsub_n; i++)
623 free(fn->gsub[i].pats);
624 for (i = 0; i < fn->gpos_n; i++)
625 free(fn->gpos[i].pats);
626 dict_free(fn->gl_dict);
627 dict_free(fn->ch_dict);
628 dict_free(fn->ch_map);
629 iset_free(fn->gsub0);
630 iset_free(fn->gpos0);
631 iset_free(fn->ggrp);
632 free(fn->gsub);
633 free(fn->gpos);
634 free(fn->gl);
635 free(fn);
638 int font_special(struct font *fn)
640 return fn->special;
643 /* return width w for the given font and size */
644 int font_wid(struct font *fn, int sz, int w)
646 sz = font_zoom(fn, sz);
647 return w >= 0 ? DEVWID(sz, w) : -DEVWID(sz, -w);
650 /* return track kerning width for the given size */
651 static int font_twid(struct font *fn, int sz)
653 if (fn->s1 >= 0 && sz <= fn->s1)
654 return fn->n1 * SC_PT;
655 if (fn->s2 >= 0 && sz >= fn->s2)
656 return fn->n2 * SC_PT;
657 if (sz > fn->s1 && sz < fn->s2)
658 return ((sz - fn->s1) * fn->n1 + (fn->s2 - sz) * fn->n2) *
659 (long) SC_PT / (fn->s2 - fn->s1);
660 return 0;
663 /* glyph width, where cfn is the current font and fn is glyph's font */
664 int font_gwid(struct font *fn, struct font *cfn, int sz, int w)
666 struct font *xfn = cfn ? cfn : fn;
667 if (xfn->cs)
668 return xfn->cs * (font_zoom(fn, xfn->cs_ps ? xfn->cs_ps : sz)
669 * SC_IN / 72) / 36;
670 return font_wid(fn, sz, w) + (cfn ? font_twid(fn, sz) : 0) +
671 (font_getbd(xfn) ? font_getbd(xfn) - 1 : 0);
674 /* space width for the give word space or sentence space */
675 int font_swid(struct font *fn, int sz, int ss)
677 return font_gwid(fn, NULL, sz, (fn->spacewid * ss + 6) / 12);
680 int font_getcs(struct font *fn)
682 return fn->cs;
685 void font_setcs(struct font *fn, int cs, int ps)
687 fn->cs = cs;
688 fn->cs_ps = ps;
691 int font_getbd(struct font *fn)
693 return fn->bd;
696 void font_setbd(struct font *fn, int bd)
698 fn->bd = bd;
701 void font_track(struct font *fn, int s1, int n1, int s2, int n2)
703 fn->s1 = s1;
704 fn->n1 = n1;
705 fn->s2 = s2;
706 fn->n2 = n2;
709 int font_zoom(struct font *fn, int sz)
711 return fn->zoom ? (sz * fn->zoom + 500) / 1000 : sz;
714 void font_setzoom(struct font *fn, int zoom)
716 fn->zoom = zoom;
719 /* enable/disable font features; returns the previous value */
720 int font_feat(struct font *fn, char *name, int val)
722 int idx = font_findfeat(fn, name);
723 int old = idx >= 0 ? fn->feat_set[idx] : 0;
724 if (idx >= 0)
725 fn->feat_set[idx] = val != 0;
726 return old;
729 /* set font script */
730 void font_scrp(struct font *fn, char *name)
732 fn->scrp = name ? font_findscrp(fn, name) : -1;