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 */
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
, lang
; /* rule's feature and script */
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 char lang_name
[NLANGS
][8]; /* language names */
45 int lang
; /* current language */
46 /* glyph substitution and positioning */
47 struct grule
*gsub
; /* glyph substitution rules */
49 struct grule
*gpos
; /* glyph positioning rules */
51 struct iset
*gsub0
; /* rules matching a glyph at pos 0 */
52 struct iset
*gpos0
; /* rules matching a glyph at pos 0 */
53 struct iset
*ggrp
; /* glyph groups */
56 /* find a glyph by its name */
57 struct glyph
*font_find(struct font
*fn
, char *name
)
59 int i
= dict_get(fn
->ch_map
, name
);
60 if (i
== -1) /* -2 means the glyph has been unmapped */
61 i
= dict_get(fn
->ch_dict
, name
);
62 return i
>= 0 ? fn
->gl
+ i
: NULL
;
65 /* find a glyph by its device-dependent identifier */
66 struct glyph
*font_glyph(struct font
*fn
, char *id
)
68 int i
= dict_get(fn
->gl_dict
, id
);
69 return i
>= 0 ? &fn
->gl
[i
] : NULL
;
72 static int font_glyphput(struct font
*fn
, char *id
, char *name
, int type
)
75 if (fn
->gl_n
== fn
->gl_sz
) {
76 fn
->gl_sz
= fn
->gl_sz
+ 1024;
77 fn
->gl
= mextend(fn
->gl
, fn
->gl_n
, fn
->gl_sz
, sizeof(fn
->gl
[0]));
79 g
= &fn
->gl
[fn
->gl_n
];
80 snprintf(g
->id
, sizeof(g
->id
), "%s", id
);
81 snprintf(g
->name
, sizeof(g
->name
), "%s", name
);
84 dict_put(fn
->gl_dict
, g
->id
, fn
->gl_n
);
88 /* map character name to the given glyph; remove the mapping if id is NULL */
89 int font_map(struct font
*fn
, char *name
, char *id
)
93 gidx
= font_glyph(fn
, id
) ? font_glyph(fn
, id
) - fn
->gl
: -2;
94 dict_put(fn
->ch_map
, name
, gidx
);
98 /* return nonzero if character name has been mapped with font_map() */
99 int font_mapped(struct font
*fn
, char *name
)
101 return dict_get(fn
->ch_map
, name
) != -1;
104 static int font_findfeat(struct font
*fn
, char *feat
);
106 /* enable/disable ligatures; first bit for liga and the second bit for rlig */
107 static int font_featlg(struct font
*fn
, int val
)
110 ret
|= font_feat(fn
, "liga", val
& 1);
111 ret
|= font_feat(fn
, "rlig", val
& 2) << 1;
115 /* enable/disable pairwise kerning */
116 static int font_featkn(struct font
*fn
, int val
)
118 return font_feat(fn
, "kern", val
);
121 /* glyph index in fn->glyphs[] */
122 static int font_idx(struct font
*fn
, struct glyph
*g
)
124 return g
? g
- fn
->gl
: -1;
127 static int font_gpatmatch(struct font
*fn
, struct gpat
*p
, int g
)
130 if (!(p
->flg
& GF_GRP
))
132 r
= iset_get(fn
->ggrp
, p
->g
);
139 static int font_rulematch(struct font
*fn
, struct grule
*rule
,
140 int *src
, int slen
, int *dst
, int dlen
)
142 int sidx
= 0; /* the index of matched glyphs in src */
143 int ncon
= 0; /* number of initial context glyphs */
144 struct gpat
*pats
= rule
->pats
;
146 /* enable only the active script if set */
147 if (fn
->scrp
>= 0 && fn
->scrp
!= rule
->scrp
)
149 /* enable common script features and those in the active language */
150 if (rule
->lang
>= 0 && fn
->lang
!= rule
->lang
)
152 if (!fn
->feat_set
[rule
->feat
])
154 /* the number of initial context glyphs */
155 for (j
= 0; j
< rule
->len
&& pats
[j
].flg
& GF_CON
; j
++)
159 /* matching the base pattern */
160 for (; j
< rule
->len
; j
++) {
161 if (pats
[j
].flg
& GF_REP
)
163 if (sidx
>= slen
|| !font_gpatmatch(fn
, &pats
[j
], src
[sidx
]))
167 /* matching the initial context */
168 for (j
= 0; j
< rule
->len
&& pats
[j
].flg
& GF_CON
; j
++)
169 if (!font_gpatmatch(fn
, &pats
[j
], dst
[j
- ncon
]))
174 /* find a matching gsub/gpos rule; *idx should be -1 initially */
175 static int font_findrule(struct font
*fn
, int gsub
, int pos
,
176 int *fwd
, int fwdlen
, int *ctx
, int ctxlen
, int *idx
)
178 struct grule
*rules
= gsub
? fn
->gsub
: fn
->gpos
;
179 int *r1
= iset_get(gsub
? fn
->gsub0
: fn
->gpos0
, fwd
[0]);
180 while (r1
&& r1
[++*idx
] >= 0) {
181 if (r1
[*idx
] >= pos
&& font_rulematch(fn
, &rules
[r1
[*idx
]],
182 fwd
, fwdlen
, ctx
, ctxlen
))
188 /* perform all possible gpos rules on src */
189 static void font_performgpos(struct font
*fn
, int *src
, int slen
,
190 int *x
, int *y
, int *xadv
, int *yadv
)
192 struct grule
*gpos
= fn
->gpos
;
194 int curs_feat
= dir_do
? font_findfeat(fn
, "curs") : -1;
198 for (i
= 0; i
< slen
; i
++) {
202 int r
= font_findrule(fn
, 0, 0, src
+ i
, slen
- i
,
207 for (k
= 0; k
< gpos
[r
].len
; k
++) {
208 x
[i
+ k
] += pats
[k
].x
;
209 y
[i
+ k
] += pats
[k
].y
;
210 xadv
[i
+ k
] += pats
[k
].xadv
;
211 yadv
[i
+ k
] += pats
[k
].yadv
;
213 if (gpos
[r
].feat
== curs_feat
) {
217 for (k
= 0; k
< gpos
[r
].len
; k
++)
218 curs_dif
+= pats
[k
].yadv
;
221 if (curs_beg
>= 0 && !curs_cur
) {
222 yadv
[curs_beg
] -= curs_dif
;
228 yadv
[curs_beg
] -= curs_dif
;
231 /* find the first gsub rule after pos that matches any glyph in src */
232 static int font_firstgsub(struct font
*fn
, int pos
, int *src
, int slen
)
236 for (i
= 0; i
< slen
; i
++) {
238 int r
= font_findrule(fn
, 1, pos
, src
+ i
, slen
- i
,
240 if (r
>= 0 && (best
< 0 || r
< best
))
246 /* apply the given gsub rule to all matches in src */
247 static int font_gsubapply(struct font
*fn
, struct grule
*rule
,
248 int *src
, int slen
, int *smap
)
254 memset(dmap
, 0, slen
* sizeof(dmap
[i
]));
255 for (i
= 0; i
< slen
; i
++) {
256 dmap
[dlen
] = smap
[i
];
257 if (font_rulematch(fn
, rule
, src
+ i
, slen
- i
,
259 for (j
= 0; j
< rule
->len
; j
++) {
260 if (rule
->pats
[j
].flg
& GF_REP
)
261 dst
[dlen
++] = rule
->pats
[j
].g
;
262 if (rule
->pats
[j
].flg
& GF_PAT
)
267 dst
[dlen
++] = src
[i
];
270 memcpy(src
, dst
, dlen
* sizeof(dst
[0]));
271 memcpy(smap
, dmap
, dlen
* sizeof(dmap
[0]));
275 /* perform all possible gsub rules on src */
276 static int font_performgsub(struct font
*fn
, int *src
, int slen
, int *smap
)
280 if ((i
= font_firstgsub(fn
, i
, src
, slen
)) < 0)
282 slen
= font_gsubapply(fn
, &fn
->gsub
[i
], src
, slen
, smap
);
287 int font_layout(struct font
*fn
, struct glyph
**gsrc
, int nsrc
, int sz
,
288 struct glyph
**gdst
, int *dmap
,
289 int *x
, int *y
, int *xadv
, int *yadv
, int lg
, int kn
)
295 /* initialising dst */
296 for (i
= 0; i
< nsrc
; i
++)
297 dst
[i
] = font_idx(fn
, gsrc
[i
]);
298 for (i
= 0; i
< ndst
; i
++)
300 memset(x
, 0, ndst
* sizeof(x
[0]));
301 memset(y
, 0, ndst
* sizeof(y
[0]));
302 memset(xadv
, 0, ndst
* sizeof(xadv
[0]));
303 memset(yadv
, 0, ndst
* sizeof(yadv
[0]));
304 /* substitution rules */
306 featlg
= font_featlg(fn
, 3);
307 ndst
= font_performgsub(fn
, dst
, ndst
, dmap
);
309 font_featlg(fn
, featlg
);
310 /* positioning rules */
312 featkn
= font_featkn(fn
, 1);
313 font_performgpos(fn
, dst
, ndst
, x
, y
, xadv
, yadv
);
315 font_featkn(fn
, featkn
);
316 for (i
= 0; i
< ndst
; i
++)
317 gdst
[i
] = fn
->gl
+ dst
[i
];
321 static int font_readchar(struct font
*fn
, FILE *fin
, int *n
, int *gid
)
328 if (fscanf(fin
, "%s %s", name
, tok
) != 2)
330 if (!strcmp("---", name
))
331 sprintf(name
, "c%04d", *n
);
332 if (strcmp("\"", tok
)) {
333 if (fscanf(fin
, "%d %s", &type
, id
) != 2)
335 *gid
= font_glyphput(fn
, id
, name
, type
);
337 sscanf(tok
, "%hd,%hd,%hd,%hd,%hd", &g
->wid
,
338 &g
->llx
, &g
->lly
, &g
->urx
, &g
->ury
);
339 dict_put(fn
->ch_dict
, name
, *gid
);
342 dict_put(fn
->ch_map
, name
, *gid
);
347 static int font_findfeat(struct font
*fn
, char *feat
)
350 for (i
= 0; i
< LEN(fn
->feat_name
) && fn
->feat_name
[i
][0]; i
++)
351 if (!strcmp(feat
, fn
->feat_name
[i
]))
353 if (i
< LEN(fn
->feat_name
)) {
354 snprintf(fn
->feat_name
[i
], sizeof(fn
->feat_name
[i
]), "%s", feat
);
360 static int font_findscrp(struct font
*fn
, char *scrp
)
363 for (i
= 0; i
< LEN(fn
->scrp_name
) && fn
->scrp_name
[i
][0]; i
++)
364 if (!strcmp(scrp
, fn
->scrp_name
[i
]))
366 if (i
== LEN(fn
->scrp_name
))
368 snprintf(fn
->scrp_name
[i
], sizeof(fn
->scrp_name
[i
]), "%s", scrp
);
372 static int font_findlang(struct font
*fn
, char *lang
)
375 for (i
= 0; i
< LEN(fn
->lang_name
) && fn
->lang_name
[i
][0]; i
++)
376 if (!strcmp(lang
, fn
->lang_name
[i
]))
378 if (i
== LEN(fn
->lang_name
))
380 snprintf(fn
->lang_name
[i
], sizeof(fn
->lang_name
[i
]), "%s", lang
);
384 static struct gpat
*font_gpat(struct font
*fn
, int len
)
386 struct gpat
*pats
= xmalloc(len
* sizeof(pats
[0]));
387 memset(pats
, 0, len
* sizeof(pats
[0]));
391 static struct grule
*font_gsub(struct font
*fn
, int len
, int feat
, int scrp
, int lang
)
394 struct gpat
*pats
= font_gpat(fn
, len
);
395 if (fn
->gsub_n
== fn
->gsub_sz
) {
396 fn
->gsub_sz
= fn
->gsub_sz
+ 1024;
397 fn
->gsub
= mextend(fn
->gsub
, fn
->gsub_n
, fn
->gsub_sz
,
398 sizeof(fn
->gsub
[0]));
400 rule
= &fn
->gsub
[fn
->gsub_n
++];
409 static struct grule
*font_gpos(struct font
*fn
, int len
, int feat
, int scrp
, int lang
)
412 struct gpat
*pats
= font_gpat(fn
, len
);
413 if (fn
->gpos_n
== fn
->gpos_sz
) {
414 fn
->gpos_sz
= fn
->gpos_sz
+ 1024;
415 fn
->gpos
= mextend(fn
->gpos
, fn
->gpos_n
, fn
->gpos_sz
,
416 sizeof(fn
->gpos
[0]));
418 rule
= &fn
->gpos
[fn
->gpos_n
++];
427 static int font_readgpat(struct font
*fn
, struct gpat
*p
, char *s
)
431 if (iset_len(fn
->ggrp
, p
->g
) == 1)
432 p
->g
= iset_get(fn
->ggrp
, p
->g
)[0];
436 p
->g
= font_idx(fn
, font_glyph(fn
, s
));
441 static void font_readfeat(struct font
*fn
, char *tok
, int *feat
, int *scrp
, int *lang
)
448 if (strchr(ftag
, ':')) {
449 stag
= strchr(ftag
, ':') + 1;
452 if (strchr(stag
, ':')) {
453 ltag
= strchr(stag
, ':') + 1;
457 *scrp
= font_findscrp(fn
, stag
);
459 *lang
= font_findlang(fn
, ltag
);
460 *feat
= font_findfeat(fn
, tok
);
463 static int font_readgsub(struct font
*fn
, FILE *fin
)
467 int feat
, scrp
, lang
;
469 if (fscanf(fin
, "%s %d", tok
, &n
) != 2)
471 font_readfeat(fn
, tok
, &feat
, &scrp
, &lang
);
472 rule
= font_gsub(fn
, n
, feat
, scrp
, lang
);
473 for (i
= 0; i
< n
; i
++) {
474 if (fscanf(fin
, "%s", tok
) != 1)
477 rule
->pats
[i
].flg
= GF_PAT
;
479 rule
->pats
[i
].flg
= GF_CON
;
481 rule
->pats
[i
].flg
= GF_REP
;
482 if (!tok
[0] || font_readgpat(fn
, &rule
->pats
[i
], tok
+ 1))
488 static int font_readgpos(struct font
*fn
, FILE *fin
)
493 int feat
, scrp
, lang
;
495 if (fscanf(fin
, "%s %d", tok
, &n
) != 2)
497 font_readfeat(fn
, tok
, &feat
, &scrp
, &lang
);
498 rule
= font_gpos(fn
, n
, feat
, scrp
, lang
);
499 for (i
= 0; i
< n
; i
++) {
500 if (fscanf(fin
, "%s", tok
) != 1)
502 col
= strchr(tok
, ':');
505 rule
->pats
[i
].flg
= GF_PAT
;
506 if (!tok
[0] || font_readgpat(fn
, &rule
->pats
[i
], tok
))
509 sscanf(col
+ 1, "%hd%hd%hd%hd",
510 &rule
->pats
[i
].x
, &rule
->pats
[i
].y
,
511 &rule
->pats
[i
].xadv
, &rule
->pats
[i
].yadv
);
516 static int font_readggrp(struct font
*fn
, FILE *fin
)
520 if (fscanf(fin
, "%d %d", &id
, &n
) != 2)
522 for (i
= 0; i
< n
; i
++) {
523 if (fscanf(fin
, "%s", tok
) != 1)
525 g
= font_idx(fn
, font_glyph(fn
, tok
));
527 iset_put(fn
->ggrp
, id
, g
);
532 static int font_readkern(struct font
*fn
, FILE *fin
)
534 char c1
[ILNLEN
], c2
[ILNLEN
];
537 if (fscanf(fin
, "%s %s %d", c1
, c2
, &val
) != 3)
539 rule
= font_gpos(fn
, 2, font_findfeat(fn
, "kern"), -1, -1);
540 rule
->pats
[0].g
= font_idx(fn
, font_glyph(fn
, c1
));
541 rule
->pats
[1].g
= font_idx(fn
, font_glyph(fn
, c2
));
542 rule
->pats
[0].xadv
= val
;
543 rule
->pats
[0].flg
= GF_PAT
;
544 rule
->pats
[1].flg
= GF_PAT
;
548 static void font_lig(struct font
*fn
, char *lig
)
555 while (utf8read(&s
, c
) > 0)
556 g
[n
++] = font_idx(fn
, font_find(fn
, c
));
557 rule
= font_gsub(fn
, n
+ 1, font_findfeat(fn
, "liga"), -1, -1);
558 for (j
= 0; j
< n
; j
++) {
559 rule
->pats
[j
].g
= g
[j
];
560 rule
->pats
[j
].flg
= GF_PAT
;
562 rule
->pats
[n
].g
= font_idx(fn
, font_find(fn
, lig
));
563 rule
->pats
[n
].flg
= GF_REP
;
566 static void skipline(FILE* filp
)
571 } while (c
!= '\n' && c
!= EOF
);
574 static struct gpat
*font_rulefirstpat(struct font
*fn
, struct grule
*rule
)
577 for (i
= 0; i
< rule
->len
; i
++)
578 if (!(rule
->pats
[i
].flg
& (GF_REP
| GF_CON
)))
579 return &rule
->pats
[i
];
583 static void font_isetinsert(struct font
*fn
, struct iset
*iset
, int rule
, struct gpat
*p
)
585 if (p
->flg
& GF_GRP
) {
586 int *r
= iset_get(fn
->ggrp
, p
->g
);
588 iset_put(iset
, *r
++, rule
);
591 iset_put(iset
, p
->g
, rule
);
595 struct font
*font_open(char *path
)
598 int ch_g
= -1; /* last glyph in the charset */
599 int ch_n
= 0; /* number of glyphs in the charset */
602 char ligs
[512][GNLEN
];
605 fin
= fopen(path
, "r");
608 fn
= xmalloc(sizeof(*fn
));
613 memset(fn
, 0, sizeof(*fn
));
614 fn
->gl_dict
= dict_make(-1, 1, 0);
615 fn
->ch_dict
= dict_make(-1, 1, 0);
616 fn
->ch_map
= dict_make(-1, 1, 0);
617 fn
->ggrp
= iset_make();
618 while (fscanf(fin
, "%s", tok
) == 1) {
619 if (!strcmp("char", tok
)) {
620 font_readchar(fn
, fin
, &ch_n
, &ch_g
);
621 } else if (!strcmp("kern", tok
)) {
622 font_readkern(fn
, fin
);
623 } else if (!strcmp("ligatures", tok
)) {
624 while (fscanf(fin
, "%s", ligs
[ligs_n
]) == 1) {
625 if (!strcmp("0", ligs
[ligs_n
]))
627 if (ligs_n
< LEN(ligs
))
630 } else if (!strcmp("gsub", tok
)) {
631 font_readgsub(fn
, fin
);
632 } else if (!strcmp("gpos", tok
)) {
633 font_readgpos(fn
, fin
);
634 } else if (!strcmp("ggrp", tok
)) {
635 font_readggrp(fn
, fin
);
636 } else if (!strcmp("spacewidth", tok
)) {
637 fscanf(fin
, "%d", &fn
->spacewid
);
638 } else if (!strcmp("special", tok
)) {
640 } else if (!strcmp("name", tok
)) {
641 fscanf(fin
, "%s", fn
->name
);
642 } else if (!strcmp("fontname", tok
)) {
643 fscanf(fin
, "%s", fn
->fontname
);
644 } else if (!strcmp("charset", tok
)) {
645 while (!font_readchar(fn
, fin
, &ch_n
, &ch_g
))
651 for (i
= 0; i
< ligs_n
; i
++)
652 font_lig(fn
, ligs
[i
]);
654 fn
->gsub0
= iset_make();
655 fn
->gpos0
= iset_make();
656 for (i
= 0; i
< fn
->gsub_n
; i
++)
657 font_isetinsert(fn
, fn
->gsub0
, i
,
658 font_rulefirstpat(fn
, &fn
->gsub
[i
]));
659 for (i
= 0; i
< fn
->gpos_n
; i
++)
660 font_isetinsert(fn
, fn
->gpos0
, i
,
661 font_rulefirstpat(fn
, &fn
->gpos
[i
]));
667 void font_close(struct font
*fn
)
670 for (i
= 0; i
< fn
->gsub_n
; i
++)
671 free(fn
->gsub
[i
].pats
);
672 for (i
= 0; i
< fn
->gpos_n
; i
++)
673 free(fn
->gpos
[i
].pats
);
674 dict_free(fn
->gl_dict
);
675 dict_free(fn
->ch_dict
);
676 dict_free(fn
->ch_map
);
677 iset_free(fn
->gsub0
);
678 iset_free(fn
->gpos0
);
686 int font_special(struct font
*fn
)
691 /* return width w for the given font and size */
692 int font_wid(struct font
*fn
, int sz
, int w
)
694 sz
= font_zoom(fn
, sz
);
695 return w
>= 0 ? DEVWID(sz
, w
) : -DEVWID(sz
, -w
);
698 /* return track kerning width for the given size */
699 static int font_twid(struct font
*fn
, int sz
)
701 if (fn
->s1
>= 0 && sz
<= fn
->s1
)
702 return fn
->n1
* SC_PT
;
703 if (fn
->s2
>= 0 && sz
>= fn
->s2
)
704 return fn
->n2
* SC_PT
;
705 if (sz
> fn
->s1
&& sz
< fn
->s2
)
706 return ((sz
- fn
->s1
) * fn
->n1
+ (fn
->s2
- sz
) * fn
->n2
) *
707 (long) SC_PT
/ (fn
->s2
- fn
->s1
);
711 /* glyph width, where cfn is the current font and fn is glyph's font */
712 int font_gwid(struct font
*fn
, struct font
*cfn
, int sz
, int w
)
714 struct font
*xfn
= cfn
? cfn
: fn
;
716 return xfn
->cs
* (font_zoom(fn
, xfn
->cs_ps
? xfn
->cs_ps
: sz
)
718 return font_wid(fn
, sz
, w
) + (cfn
? font_twid(fn
, sz
) : 0) +
719 (font_getbd(xfn
) ? font_getbd(xfn
) - 1 : 0);
722 /* space width for the give word space or sentence space */
723 int font_swid(struct font
*fn
, int sz
, int ss
)
725 return font_gwid(fn
, NULL
, sz
, (fn
->spacewid
* ss
+ 6) / 12);
728 int font_getcs(struct font
*fn
)
733 void font_setcs(struct font
*fn
, int cs
, int ps
)
739 int font_getbd(struct font
*fn
)
744 void font_setbd(struct font
*fn
, int bd
)
749 void font_track(struct font
*fn
, int s1
, int n1
, int s2
, int n2
)
757 int font_zoom(struct font
*fn
, int sz
)
759 return fn
->zoom
? (sz
* fn
->zoom
+ 500) / 1000 : sz
;
762 void font_setzoom(struct font
*fn
, int zoom
)
767 /* enable/disable font features; returns the previous value */
768 int font_feat(struct font
*fn
, char *name
, int val
)
770 int idx
= font_findfeat(fn
, name
);
771 int old
= idx
>= 0 ? fn
->feat_set
[idx
] : 0;
773 fn
->feat_set
[idx
] = val
!= 0;
777 /* set font script */
778 void font_scrp(struct font
*fn
, char *name
)
780 fn
->scrp
= name
? font_findscrp(fn
, name
) : -1;
783 /* set font language */
784 void font_lang(struct font
*fn
, char *name
)
786 fn
->lang
= name
? font_findlang(fn
, name
) : -1;