font: read the script assigned to each rule
[neatroff.git] / font.c
blob29e4bbaf2d30aada37659c49c51c8960c6639cc4
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; /* feature owning this rule */
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 */
40 char feat_name[NFEATS][8]; /* feature names */
41 int feat_set[NFEATS]; /* feature enabled */
42 int feat_n;
43 /* glyph substitution and positioning */
44 struct grule *gsub; /* glyph substitution rules */
45 int gsub_n, gsub_sz;
46 struct grule *gpos; /* glyph positioning rules */
47 int gpos_n, gpos_sz;
48 struct iset *gsub0; /* rules matching a glyph at pos 0 */
49 struct iset *gpos0; /* rules matching a glyph at pos 0 */
50 struct iset *ggrp; /* glyph groups */
53 /* find a glyph by its name */
54 struct glyph *font_find(struct font *fn, char *name)
56 int i = dict_get(fn->ch_map, name);
57 if (i == -1)
58 i = dict_get(fn->ch_dict, name);
59 return i >= 0 ? fn->gl + i : NULL;
62 /* find a glyph by its device-dependent identifier */
63 struct glyph *font_glyph(struct font *fn, char *id)
65 int i = dict_get(fn->gl_dict, id);
66 return i >= 0 ? &fn->gl[i] : NULL;
69 static int font_glyphput(struct font *fn, char *id, char *name, int type)
71 struct glyph *g;
72 if (fn->gl_n == fn->gl_sz) {
73 fn->gl_sz = fn->gl_sz + 1024;
74 fn->gl = mextend(fn->gl, fn->gl_n, fn->gl_sz, sizeof(fn->gl[0]));
76 g = &fn->gl[fn->gl_n];
77 snprintf(g->id, sizeof(g->id), "%s", id);
78 snprintf(g->name, sizeof(g->name), "%s", name);
79 g->type = type;
80 g->font = fn;
81 dict_put(fn->gl_dict, g->id, fn->gl_n);
82 return fn->gl_n++;
85 /* map character name to the given glyph; remove the mapping if id is NULL */
86 int font_map(struct font *fn, char *name, char *id)
88 int gidx = -1;
89 if (id)
90 gidx = font_glyph(fn, id) ? font_glyph(fn, id) - fn->gl : -2;
91 dict_put(fn->ch_map, name, gidx);
92 return 0;
95 /* return nonzero if character name has been mapped with font_map() */
96 int font_mapped(struct font *fn, char *name)
98 return dict_get(fn->ch_map, name) != -1;
101 /* enable/disable ligatures; first bit for liga and the second bit for rlig */
102 static int font_featlg(struct font *fn, int val)
104 int ret = 0;
105 ret |= font_feat(fn, "liga", val & 1);
106 ret |= font_feat(fn, "rlig", val & 2) << 1;
107 return ret;
110 /* enable/disable pairwise kerning */
111 static int font_featkn(struct font *fn, int val)
113 return font_feat(fn, "kern", val);
116 /* glyph index in fn->glyphs[] */
117 static int font_idx(struct font *fn, struct glyph *g)
119 return g ? g - fn->gl : -1;
122 static int font_gpatmatch(struct font *fn, struct gpat *p, int g)
124 int *r;
125 if (!(p->flg & GF_GRP))
126 return p->g == g;
127 r = iset_get(fn->ggrp, p->g);
128 while (r && *r >= 0)
129 if (*r++ == g)
130 return 1;
131 return 0;
134 static int font_rulematch(struct font *fn, struct grule *rule,
135 int *src, int slen, int *dst, int dlen)
137 int sidx = 0; /* the index of matched glyphs in src */
138 int ncon = 0; /* number of initial context glyphs */
139 struct gpat *pats = rule->pats;
140 int j;
141 if (!fn->feat_set[rule->feat])
142 return 0;
143 /* the number of initial context glyphs */
144 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
145 ncon++;
146 if (dlen < ncon)
147 return 0;
148 /* matching the base pattern */
149 for (; j < rule->len; j++) {
150 if (pats[j].flg & GF_REP)
151 continue;
152 if (sidx >= slen || !font_gpatmatch(fn, &pats[j], src[sidx]))
153 return 0;
154 sidx++;
156 /* matching the initial context */
157 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
158 if (!font_gpatmatch(fn, &pats[j], dst[j - ncon]))
159 return 0;
160 return 1;
163 /* find a matching gsub/gpos rule; *idx should be -1 initially */
164 static int font_findrule(struct font *fn, int gsub, int pos,
165 int *fwd, int fwdlen, int *ctx, int ctxlen, int *idx)
167 struct grule *rules = gsub ? fn->gsub : fn->gpos;
168 int *r1 = iset_get(gsub ? fn->gsub0 : fn->gpos0, fwd[0]);
169 while (r1 && r1[++*idx] >= 0) {
170 if (r1[*idx] >= pos && font_rulematch(fn, &rules[r1[*idx]],
171 fwd, fwdlen, ctx, ctxlen))
172 return r1[*idx];
174 return -1;
177 /* perform all possible gpos rules on src */
178 static void font_performgpos(struct font *fn, int *src, int slen,
179 int *x, int *y, int *xadv, int *yadv)
181 struct grule *gpos = fn->gpos;
182 struct gpat *pats;
183 int i, k;
184 for (i = 0; i < slen; i++) {
185 int idx = -1;
186 while (1) {
187 int r = font_findrule(fn, 0, 0, src + i, slen - i,
188 src + i, i, &idx);
189 if (r < 0)
190 break;
191 pats = gpos[r].pats;
192 /* we should accumulate the values... */
193 for (k = 0; k < gpos[r].len; k++) {
194 x[i + k] += pats[k].x;
195 y[i + k] += pats[k].y;
196 xadv[i + k] += pats[k].xadv;
197 yadv[i + k] += pats[k].yadv;
203 /* find the first gsub rule after pos that matches any glyph in src */
204 static int font_firstgsub(struct font *fn, int pos, int *src, int slen)
206 int best = -1;
207 int i;
208 for (i = 0; i < slen; i++) {
209 int idx = -1;
210 int r = font_findrule(fn, 1, pos, src + i, slen - i,
211 src + i, i, &idx);
212 if (r >= 0 && (best < 0 || r < best))
213 best = r;
215 return best;
218 /* apply the given gsub rule to all matches in src */
219 static int font_gsubapply(struct font *fn, struct grule *rule,
220 int *src, int slen, int *smap)
222 int dst[WORDLEN];
223 int dlen = 0;
224 int dmap[WORDLEN];
225 int i, j;
226 memset(dmap, 0, slen * sizeof(dmap[i]));
227 for (i = 0; i < slen; i++) {
228 dmap[dlen] = smap[i];
229 if (font_rulematch(fn, rule, src + i, slen - i,
230 dst + dlen, dlen)) {
231 for (j = 0; j < rule->len; j++) {
232 if (rule->pats[j].flg & GF_REP)
233 dst[dlen++] = rule->pats[j].g;
234 if (rule->pats[j].flg & GF_PAT)
235 i++;
237 i--;
238 } else {
239 dst[dlen++] = src[i];
242 memcpy(src, dst, dlen * sizeof(dst[0]));
243 memcpy(smap, dmap, dlen * sizeof(dmap[0]));
244 return dlen;
247 /* perform all possible gsub rules on src */
248 static int font_performgsub(struct font *fn, int *src, int slen, int *smap)
250 int i = -1;
251 while (++i >= 0) {
252 if ((i = font_firstgsub(fn, i, src, slen)) < 0)
253 break;
254 slen = font_gsubapply(fn, &fn->gsub[i], src, slen, smap);
256 return slen;
259 int font_layout(struct font *fn, struct glyph **gsrc, int nsrc, int sz,
260 struct glyph **gdst, int *dmap,
261 int *x, int *y, int *xadv, int *yadv, int lg, int kn)
263 int dst[WORDLEN];
264 int ndst = nsrc;
265 int i;
266 int featlg, featkn;
267 /* initialising dst */
268 for (i = 0; i < nsrc; i++)
269 dst[i] = font_idx(fn, gsrc[i]);
270 for (i = 0; i < ndst; i++)
271 dmap[i] = i;
272 memset(x, 0, ndst * sizeof(x[0]));
273 memset(y, 0, ndst * sizeof(y[0]));
274 memset(xadv, 0, ndst * sizeof(xadv[0]));
275 memset(yadv, 0, ndst * sizeof(yadv[0]));
276 /* substitution rules */
277 if (lg)
278 featlg = font_featlg(fn, 3);
279 ndst = font_performgsub(fn, dst, ndst, dmap);
280 if (lg)
281 font_featlg(fn, featlg);
282 /* positioning rules */
283 if (kn)
284 featkn = font_featkn(fn, 1);
285 font_performgpos(fn, dst, ndst, x, y, xadv, yadv);
286 if (kn)
287 font_featkn(fn, featkn);
288 for (i = 0; i < ndst; i++)
289 gdst[i] = fn->gl + dst[i];
290 return ndst;
293 static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid)
295 struct glyph *g;
296 char tok[ILNLEN];
297 char name[ILNLEN];
298 char id[ILNLEN];
299 int type;
300 if (fscanf(fin, "%s %s", name, tok) != 2)
301 return 1;
302 if (!strcmp("---", name))
303 sprintf(name, "c%04d", *n);
304 if (strcmp("\"", tok)) {
305 if (fscanf(fin, "%d %s", &type, id) != 2)
306 return 1;
307 *gid = dict_get(fn->gl_dict, id);
308 if (*gid < 0) {
309 *gid = font_glyphput(fn, id, name, type);
310 g = &fn->gl[*gid];
311 sscanf(tok, "%hd,%hd,%hd,%hd,%hd", &g->wid,
312 &g->llx, &g->lly, &g->urx, &g->ury);
315 dict_put(fn->ch_dict, name, *gid);
316 (*n)++;
317 return 0;
320 static int font_findfeat(struct font *fn, char *feat, int mk)
322 int i;
323 for (i = 0; i < fn->feat_n; i++)
324 if (!strcmp(feat, fn->feat_name[i]))
325 return i;
326 if (mk) {
327 snprintf(fn->feat_name[fn->feat_n],
328 sizeof(fn->feat_name[fn->feat_n]), "%s", feat);
330 return mk ? fn->feat_n++ : -1;
333 static struct gpat *font_gpat(struct font *fn, int len)
335 struct gpat *pats = xmalloc(len * sizeof(pats[0]));
336 memset(pats, 0, len * sizeof(pats[0]));
337 return pats;
340 static struct grule *font_gsub(struct font *fn, char *feat, int len)
342 struct grule *rule;
343 struct gpat *pats = font_gpat(fn, len);
344 if (fn->gsub_n == fn->gsub_sz) {
345 fn->gsub_sz = fn->gsub_sz + 1024;
346 fn->gsub = mextend(fn->gsub, fn->gsub_n, fn->gsub_sz,
347 sizeof(fn->gsub[0]));
349 rule = &fn->gsub[fn->gsub_n++];
350 rule->pats = pats;
351 rule->len = len;
352 rule->feat = font_findfeat(fn, feat, 1);
353 return rule;
356 static struct grule *font_gpos(struct font *fn, char *feat, int len)
358 struct grule *rule;
359 struct gpat *pats = font_gpat(fn, len);
360 if (fn->gpos_n == fn->gpos_sz) {
361 fn->gpos_sz = fn->gpos_sz + 1024;
362 fn->gpos = mextend(fn->gpos, fn->gpos_n, fn->gpos_sz,
363 sizeof(fn->gpos[0]));
365 rule = &fn->gpos[fn->gpos_n++];
366 rule->pats = pats;
367 rule->len = len;
368 rule->feat = font_findfeat(fn, feat, 1);
369 return rule;
372 static int font_readgpat(struct font *fn, struct gpat *p, char *s)
374 if (s[0] == '@') {
375 p->g = atoi(s + 1);
376 if (iset_len(fn->ggrp, p->g) == 1)
377 p->g = iset_get(fn->ggrp, p->g)[0];
378 else
379 p->flg |= GF_GRP;
380 } else {
381 p->g = font_idx(fn, font_glyph(fn, s));
383 return p->g < 0;
386 static int font_readgsub(struct font *fn, FILE *fin)
388 char tok[128];
389 struct grule *rule;
390 int i, n;
391 if (fscanf(fin, "%s %d", tok, &n) != 2)
392 return 1;
393 if (strchr(tok, ':')) /* "feature:script" */
394 strchr(tok, ':')[0] = '\0';
395 rule = font_gsub(fn, tok, n);
396 for (i = 0; i < n; i++) {
397 if (fscanf(fin, "%s", tok) != 1)
398 return 1;
399 if (tok[0] == '-')
400 rule->pats[i].flg = GF_PAT;
401 if (tok[0] == '=')
402 rule->pats[i].flg = GF_CON;
403 if (tok[0] == '+')
404 rule->pats[i].flg = GF_REP;
405 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok + 1))
406 return 0;
408 return 0;
411 static int font_readgpos(struct font *fn, FILE *fin)
413 char tok[128];
414 char *col;
415 struct grule *rule;
416 int i, n;
417 if (fscanf(fin, "%s %d", tok, &n) != 2)
418 return 1;
419 if (strchr(tok, ':')) /* "feature:script" */
420 strchr(tok, ':')[0] = '\0';
421 rule = font_gpos(fn, tok, n);
422 for (i = 0; i < n; i++) {
423 if (fscanf(fin, "%s", tok) != 1)
424 return 1;
425 col = strchr(tok, ':');
426 if (col)
427 *col = '\0';
428 rule->pats[i].flg = GF_PAT;
429 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok))
430 return 0;
431 if (col)
432 sscanf(col + 1, "%hd%hd%hd%hd",
433 &rule->pats[i].x, &rule->pats[i].y,
434 &rule->pats[i].xadv, &rule->pats[i].yadv);
436 return 0;
439 static int font_readggrp(struct font *fn, FILE *fin)
441 char tok[ILNLEN];
442 int id, n, i, g;
443 if (fscanf(fin, "%d %d", &id, &n) != 2)
444 return 1;
445 for (i = 0; i < n; i++) {
446 if (fscanf(fin, "%s", tok) != 1)
447 return 1;
448 g = font_idx(fn, font_glyph(fn, tok));
449 if (g >= 0)
450 iset_put(fn->ggrp, id, g);
452 return 0;
455 static int font_readkern(struct font *fn, FILE *fin)
457 char c1[ILNLEN], c2[ILNLEN];
458 struct grule *rule;
459 int val;
460 if (fscanf(fin, "%s %s %d", c1, c2, &val) != 3)
461 return 1;
462 rule = font_gpos(fn, "kern", 2);
463 rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
464 rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
465 rule->pats[0].xadv = val;
466 rule->pats[0].flg = GF_PAT;
467 rule->pats[1].flg = GF_PAT;
468 return 0;
471 static void font_lig(struct font *fn, char *lig)
473 char c[GNLEN];
474 int g[WORDLEN];
475 struct grule *rule;
476 char *s = lig;
477 int j, n = 0;
478 while (utf8read(&s, c) > 0)
479 g[n++] = font_idx(fn, font_find(fn, c));
480 rule = font_gsub(fn, "liga", n + 1);
481 for (j = 0; j < n; j++) {
482 rule->pats[j].g = g[j];
483 rule->pats[j].flg = GF_PAT;
485 rule->pats[n].g = font_idx(fn, font_find(fn, lig));
486 rule->pats[n].flg = GF_REP;
489 static void skipline(FILE* filp)
491 int c;
492 do {
493 c = getc(filp);
494 } while (c != '\n' && c != EOF);
497 static struct gpat *font_rulefirstpat(struct font *fn, struct grule *rule)
499 int i;
500 for (i = 0; i < rule->len; i++)
501 if (!(rule->pats[i].flg & (GF_REP | GF_CON)))
502 return &rule->pats[i];
503 return NULL;
506 static void font_isetinsert(struct font *fn, struct iset *iset, int rule, struct gpat *p)
508 if (p->flg & GF_GRP) {
509 int *r = iset_get(fn->ggrp, p->g);
510 while (r && *r >= 0)
511 iset_put(iset, *r++, rule);
512 } else {
513 if (p->g >= 0)
514 iset_put(iset, p->g, rule);
518 struct font *font_open(char *path)
520 struct font *fn;
521 int ch_g = -1; /* last glyph in the charset */
522 int ch_n = 0; /* number of glyphs in the charset */
523 char tok[ILNLEN];
524 FILE *fin;
525 char ligs[512][GNLEN];
526 int ligs_n = 0;
527 int i;
528 fin = fopen(path, "r");
529 if (!fin)
530 return NULL;
531 fn = xmalloc(sizeof(*fn));
532 if (!fn) {
533 fclose(fin);
534 return NULL;
536 memset(fn, 0, sizeof(*fn));
537 fn->gl_dict = dict_make(-1, 1, 0);
538 fn->ch_dict = dict_make(-1, 1, 0);
539 fn->ch_map = dict_make(-1, 1, 0);
540 fn->ggrp = iset_make();
541 while (fscanf(fin, "%s", tok) == 1) {
542 if (!strcmp("char", tok)) {
543 font_readchar(fn, fin, &ch_n, &ch_g);
544 } else if (!strcmp("kern", tok)) {
545 font_readkern(fn, fin);
546 } else if (!strcmp("ligatures", tok)) {
547 while (fscanf(fin, "%s", ligs[ligs_n]) == 1) {
548 if (!strcmp("0", ligs[ligs_n]))
549 break;
550 if (ligs_n < LEN(ligs))
551 ligs_n++;
553 } else if (!strcmp("gsub", tok)) {
554 font_readgsub(fn, fin);
555 } else if (!strcmp("gpos", tok)) {
556 font_readgpos(fn, fin);
557 } else if (!strcmp("ggrp", tok)) {
558 font_readggrp(fn, fin);
559 } else if (!strcmp("spacewidth", tok)) {
560 fscanf(fin, "%d", &fn->spacewid);
561 } else if (!strcmp("special", tok)) {
562 fn->special = 1;
563 } else if (!strcmp("name", tok)) {
564 fscanf(fin, "%s", fn->name);
565 } else if (!strcmp("fontname", tok)) {
566 fscanf(fin, "%s", fn->fontname);
567 } else if (!strcmp("charset", tok)) {
568 while (!font_readchar(fn, fin, &ch_n, &ch_g))
570 break;
572 skipline(fin);
574 for (i = 0; i < ligs_n; i++)
575 font_lig(fn, ligs[i]);
576 fclose(fin);
577 fn->gsub0 = iset_make();
578 fn->gpos0 = iset_make();
579 for (i = 0; i < fn->gsub_n; i++)
580 font_isetinsert(fn, fn->gsub0, i,
581 font_rulefirstpat(fn, &fn->gsub[i]));
582 for (i = 0; i < fn->gpos_n; i++)
583 font_isetinsert(fn, fn->gpos0, i,
584 font_rulefirstpat(fn, &fn->gpos[i]));
585 return fn;
588 void font_close(struct font *fn)
590 int i;
591 for (i = 0; i < fn->gsub_n; i++)
592 free(fn->gsub[i].pats);
593 for (i = 0; i < fn->gpos_n; i++)
594 free(fn->gpos[i].pats);
595 dict_free(fn->gl_dict);
596 dict_free(fn->ch_dict);
597 dict_free(fn->ch_map);
598 iset_free(fn->gsub0);
599 iset_free(fn->gpos0);
600 iset_free(fn->ggrp);
601 free(fn->gsub);
602 free(fn->gpos);
603 free(fn->gl);
604 free(fn);
607 int font_special(struct font *fn)
609 return fn->special;
612 /* return width w for the given font and size */
613 int font_wid(struct font *fn, int sz, int w)
615 sz = font_zoom(fn, sz);
616 return w >= 0 ? DEVWID(sz, w) : -DEVWID(sz, -w);
619 /* return track kerning width for the given size */
620 static int font_twid(struct font *fn, int sz)
622 if (fn->s1 >= 0 && sz <= fn->s1)
623 return fn->n1 * SC_PT;
624 if (fn->s2 >= 0 && sz >= fn->s2)
625 return fn->n2 * SC_PT;
626 if (sz > fn->s1 && sz < fn->s2)
627 return ((sz - fn->s1) * fn->n1 + (fn->s2 - sz) * fn->n2) *
628 (long) SC_PT / (fn->s2 - fn->s1);
629 return 0;
632 /* glyph width, where cfn is the current font and fn is glyph's font */
633 int font_gwid(struct font *fn, struct font *cfn, int sz, int w)
635 struct font *xfn = cfn ? cfn : fn;
636 if (xfn->cs)
637 return xfn->cs * (font_zoom(fn, xfn->cs_ps ? xfn->cs_ps : sz)
638 * SC_IN / 72) / 36;
639 return font_wid(fn, sz, w) + (cfn ? font_twid(fn, sz) : 0) +
640 (font_getbd(xfn) ? font_getbd(xfn) - 1 : 0);
643 /* space width for the give word space or sentence space */
644 int font_swid(struct font *fn, int sz, int ss)
646 return font_gwid(fn, NULL, sz, (fn->spacewid * ss + 6) / 12);
649 int font_getcs(struct font *fn)
651 return fn->cs;
654 void font_setcs(struct font *fn, int cs, int ps)
656 fn->cs = cs;
657 fn->cs_ps = ps;
660 int font_getbd(struct font *fn)
662 return fn->bd;
665 void font_setbd(struct font *fn, int bd)
667 fn->bd = bd;
670 void font_track(struct font *fn, int s1, int n1, int s2, int n2)
672 fn->s1 = s1;
673 fn->n1 = n1;
674 fn->s2 = s2;
675 fn->n2 = n2;
678 int font_zoom(struct font *fn, int sz)
680 return fn->zoom ? (sz * fn->zoom + 500) / 1000 : sz;
683 void font_setzoom(struct font *fn, int zoom)
685 fn->zoom = zoom;
688 /* enable/disable font features; returns the previous value */
689 int font_feat(struct font *fn, char *name, int val)
691 int idx = font_findfeat(fn, name, 0);
692 int old = idx >= 0 ? fn->feat_set[idx] : 0;
693 if (idx >= 0)
694 fn->feat_set[idx] = val != 0;
695 return old;