fmt: support stretchable no-break space (\~)
[neatroff.git] / font.c
blobce9e445a1ec10f53f1f49aa3e677bce3b6e529bc
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 static int font_findrule(struct font *fn, int gsub, int pos,
164 int *fwd, int fwdlen, int *ctx, int ctxlen)
166 struct grule *rules = gsub ? fn->gsub : fn->gpos;
167 int *r1 = iset_get(gsub ? fn->gsub0 : fn->gpos0, fwd[0]);
168 int i = -1;
169 while (r1 && r1[++i] >= 0) {
170 if (r1[i] >= pos && font_rulematch(fn, &rules[r1[i]], fwd,
171 fwdlen, ctx, ctxlen))
172 return r1[i];
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 int i, k;
183 for (i = 0; i < slen; i++) {
184 int r = font_findrule(fn, 0, 0, src + i, slen - i, src + i, i);
185 if (r >= 0) {
186 struct gpat *pats = gpos[r].pats;
187 /* we should accumulate the values... */
188 for (k = 0; k < gpos[r].len; k++) {
189 x[i + k] = pats[k].x;
190 y[i + k] = pats[k].y;
191 xadv[i + k] = pats[k].xadv;
192 yadv[i + k] = pats[k].yadv;
198 /* find the first gsub rule after pos that matches any glyph in src */
199 static int font_firstgsub(struct font *fn, int pos, int *src, int slen)
201 int best = -1;
202 int i;
203 for (i = 0; i < slen; i++) {
204 int r = font_findrule(fn, 1, pos, src + i, slen - i, src + i, i);
205 if (r >= 0 && (best < 0 || r < best))
206 best = r;
208 return best;
211 /* apply the given gsub rule to all matches in src */
212 static int font_gsubapply(struct font *fn, struct grule *rule,
213 int *src, int slen, int *smap)
215 int dst[WORDLEN];
216 int dlen = 0;
217 int dmap[WORDLEN];
218 int i, j;
219 memset(dmap, 0, slen * sizeof(dmap[i]));
220 for (i = 0; i < slen; i++) {
221 dmap[dlen] = smap[i];
222 if (font_rulematch(fn, rule, src + i, slen - i,
223 dst + dlen, dlen)) {
224 for (j = 0; j < rule->len; j++) {
225 if (rule->pats[j].flg & GF_REP)
226 dst[dlen++] = rule->pats[j].g;
227 if (rule->pats[j].flg & GF_PAT)
228 i++;
230 i--;
231 } else {
232 dst[dlen++] = src[i];
235 memcpy(src, dst, dlen * sizeof(dst[0]));
236 memcpy(smap, dmap, dlen * sizeof(dmap[0]));
237 return dlen;
240 /* perform all possible gsub rules on src */
241 static int font_performgsub(struct font *fn, int *src, int slen, int *smap)
243 int i = -1;
244 while (++i >= 0) {
245 if ((i = font_firstgsub(fn, i, src, slen)) < 0)
246 break;
247 slen = font_gsubapply(fn, &fn->gsub[i], src, slen, smap);
249 return slen;
252 int font_layout(struct font *fn, struct glyph **gsrc, int nsrc, int sz,
253 struct glyph **gdst, int *dmap,
254 int *x, int *y, int *xadv, int *yadv, int lg, int kn)
256 int dst[WORDLEN];
257 int ndst = nsrc;
258 int i;
259 int featlg, featkn;
260 /* initialising dst */
261 for (i = 0; i < nsrc; i++)
262 dst[i] = font_idx(fn, gsrc[i]);
263 for (i = 0; i < ndst; i++)
264 dmap[i] = i;
265 memset(x, 0, ndst * sizeof(x[0]));
266 memset(y, 0, ndst * sizeof(y[0]));
267 memset(xadv, 0, ndst * sizeof(xadv[0]));
268 memset(yadv, 0, ndst * sizeof(yadv[0]));
269 /* substitution rules */
270 if (lg)
271 featlg = font_featlg(fn, 3);
272 ndst = font_performgsub(fn, dst, ndst, dmap);
273 if (lg)
274 font_featlg(fn, featlg);
275 /* positioning rules */
276 if (kn)
277 featkn = font_featkn(fn, 1);
278 font_performgpos(fn, dst, ndst, x, y, xadv, yadv);
279 if (kn)
280 font_featkn(fn, featkn);
281 for (i = 0; i < ndst; i++)
282 gdst[i] = fn->gl + dst[i];
283 return ndst;
286 static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid)
288 struct glyph *g;
289 char tok[ILNLEN];
290 char name[ILNLEN];
291 char id[ILNLEN];
292 int type;
293 if (fscanf(fin, "%s %s", name, tok) != 2)
294 return 1;
295 if (!strcmp("---", name))
296 sprintf(name, "c%04d", *n);
297 if (strcmp("\"", tok)) {
298 if (fscanf(fin, "%d %s", &type, id) != 2)
299 return 1;
300 *gid = dict_get(fn->gl_dict, id);
301 if (*gid < 0) {
302 *gid = font_glyphput(fn, id, name, type);
303 g = &fn->gl[*gid];
304 sscanf(tok, "%hd,%hd,%hd,%hd,%hd", &g->wid,
305 &g->llx, &g->lly, &g->urx, &g->ury);
308 dict_put(fn->ch_dict, name, *gid);
309 (*n)++;
310 return 0;
313 static int font_findfeat(struct font *fn, char *feat, int mk)
315 int i;
316 for (i = 0; i < fn->feat_n; i++)
317 if (!strcmp(feat, fn->feat_name[i]))
318 return i;
319 if (mk) {
320 snprintf(fn->feat_name[fn->feat_n],
321 sizeof(fn->feat_name[fn->feat_n]), "%s", feat);
323 return mk ? fn->feat_n++ : -1;
326 static struct gpat *font_gpat(struct font *fn, int len)
328 struct gpat *pats = xmalloc(len * sizeof(pats[0]));
329 memset(pats, 0, len * sizeof(pats[0]));
330 return pats;
333 static struct grule *font_gsub(struct font *fn, char *feat, int len)
335 struct grule *rule;
336 struct gpat *pats = font_gpat(fn, len);
337 if (fn->gsub_n == fn->gsub_sz) {
338 fn->gsub_sz = fn->gsub_sz + 1024;
339 fn->gsub = mextend(fn->gsub, fn->gsub_n, fn->gsub_sz,
340 sizeof(fn->gsub[0]));
342 rule = &fn->gsub[fn->gsub_n++];
343 rule->pats = pats;
344 rule->len = len;
345 rule->feat = font_findfeat(fn, feat, 1);
346 return rule;
349 static struct grule *font_gpos(struct font *fn, char *feat, int len)
351 struct grule *rule;
352 struct gpat *pats = font_gpat(fn, len);
353 if (fn->gpos_n == fn->gpos_sz) {
354 fn->gpos_sz = fn->gpos_sz + 1024;
355 fn->gpos = mextend(fn->gpos, fn->gpos_n, fn->gpos_sz,
356 sizeof(fn->gpos[0]));
358 rule = &fn->gpos[fn->gpos_n++];
359 rule->pats = pats;
360 rule->len = len;
361 rule->feat = font_findfeat(fn, feat, 1);
362 return rule;
365 static int font_readgpat(struct font *fn, struct gpat *p, char *s)
367 if (s[0] == '@') {
368 p->g = atoi(s + 1);
369 if (iset_len(fn->ggrp, p->g) == 1)
370 p->g = iset_get(fn->ggrp, p->g)[0];
371 else
372 p->flg |= GF_GRP;
373 } else {
374 p->g = font_idx(fn, font_glyph(fn, s));
376 return p->g < 0;
379 static int font_readgsub(struct font *fn, FILE *fin)
381 char tok[128];
382 struct grule *rule;
383 int i, n;
384 if (fscanf(fin, "%s %d", tok, &n) != 2)
385 return 1;
386 rule = font_gsub(fn, tok, n);
387 for (i = 0; i < n; i++) {
388 if (fscanf(fin, "%s", tok) != 1)
389 return 1;
390 if (tok[0] == '-')
391 rule->pats[i].flg = GF_PAT;
392 if (tok[0] == '=')
393 rule->pats[i].flg = GF_CON;
394 if (tok[0] == '+')
395 rule->pats[i].flg = GF_REP;
396 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok + 1))
397 return 0;
399 return 0;
402 static int font_readgpos(struct font *fn, FILE *fin)
404 char tok[128];
405 char *col;
406 struct grule *rule;
407 int i, n;
408 if (fscanf(fin, "%s %d", tok, &n) != 2)
409 return 1;
410 rule = font_gpos(fn, tok, n);
411 for (i = 0; i < n; i++) {
412 if (fscanf(fin, "%s", tok) != 1)
413 return 1;
414 col = strchr(tok, ':');
415 if (col)
416 *col = '\0';
417 rule->pats[i].flg = GF_PAT;
418 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok))
419 return 0;
420 if (col)
421 sscanf(col + 1, "%hd%hd%hd%hd",
422 &rule->pats[i].x, &rule->pats[i].y,
423 &rule->pats[i].xadv, &rule->pats[i].yadv);
425 return 0;
428 static int font_readggrp(struct font *fn, FILE *fin)
430 char tok[ILNLEN];
431 int id, n, i, g;
432 if (fscanf(fin, "%d %d", &id, &n) != 2)
433 return 1;
434 for (i = 0; i < n; i++) {
435 if (fscanf(fin, "%s", tok) != 1)
436 return 1;
437 g = font_idx(fn, font_glyph(fn, tok));
438 if (g >= 0)
439 iset_put(fn->ggrp, id, g);
441 return 0;
444 static int font_readkern(struct font *fn, FILE *fin)
446 char c1[ILNLEN], c2[ILNLEN];
447 struct grule *rule;
448 int val;
449 if (fscanf(fin, "%s %s %d", c1, c2, &val) != 3)
450 return 1;
451 rule = font_gpos(fn, "kern", 2);
452 rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
453 rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
454 rule->pats[0].xadv = val;
455 rule->pats[0].flg = GF_PAT;
456 rule->pats[1].flg = GF_PAT;
457 return 0;
460 static void font_lig(struct font *fn, char *lig)
462 char c[GNLEN];
463 int g[WORDLEN];
464 struct grule *rule;
465 char *s = lig;
466 int j, n = 0;
467 while (utf8read(&s, c) > 0)
468 g[n++] = font_idx(fn, font_find(fn, c));
469 rule = font_gsub(fn, "liga", n + 1);
470 for (j = 0; j < n; j++) {
471 rule->pats[j].g = g[j];
472 rule->pats[j].flg = GF_PAT;
474 rule->pats[n].g = font_idx(fn, font_find(fn, lig));
475 rule->pats[n].flg = GF_REP;
478 static void skipline(FILE* filp)
480 int c;
481 do {
482 c = getc(filp);
483 } while (c != '\n' && c != EOF);
486 static struct gpat *font_rulefirstpat(struct font *fn, struct grule *rule)
488 int i;
489 for (i = 0; i < rule->len; i++)
490 if (!(rule->pats[i].flg & (GF_REP | GF_CON)))
491 return &rule->pats[i];
492 return NULL;
495 static void font_isetinsert(struct font *fn, struct iset *iset, int rule, struct gpat *p)
497 if (p->flg & GF_GRP) {
498 int *r = iset_get(fn->ggrp, p->g);
499 while (r && *r >= 0)
500 iset_put(iset, *r++, rule);
501 } else {
502 if (p->g >= 0)
503 iset_put(iset, p->g, rule);
507 struct font *font_open(char *path)
509 struct font *fn;
510 int ch_g = -1; /* last glyph in the charset */
511 int ch_n = 0; /* number of glyphs in the charset */
512 char tok[ILNLEN];
513 FILE *fin;
514 char ligs[512][GNLEN];
515 int ligs_n = 0;
516 int i;
517 fin = fopen(path, "r");
518 if (!fin)
519 return NULL;
520 fn = xmalloc(sizeof(*fn));
521 if (!fn) {
522 fclose(fin);
523 return NULL;
525 memset(fn, 0, sizeof(*fn));
526 fn->gl_dict = dict_make(-1, 1, 0);
527 fn->ch_dict = dict_make(-1, 1, 0);
528 fn->ch_map = dict_make(-1, 1, 0);
529 fn->ggrp = iset_make();
530 while (fscanf(fin, "%s", tok) == 1) {
531 if (!strcmp("char", tok)) {
532 font_readchar(fn, fin, &ch_n, &ch_g);
533 } else if (!strcmp("kern", tok)) {
534 font_readkern(fn, fin);
535 } else if (!strcmp("ligatures", tok)) {
536 while (fscanf(fin, "%s", ligs[ligs_n]) == 1) {
537 if (!strcmp("0", ligs[ligs_n]))
538 break;
539 if (ligs_n < LEN(ligs))
540 ligs_n++;
542 } else if (!strcmp("gsub", tok)) {
543 font_readgsub(fn, fin);
544 } else if (!strcmp("gpos", tok)) {
545 font_readgpos(fn, fin);
546 } else if (!strcmp("ggrp", tok)) {
547 font_readggrp(fn, fin);
548 } else if (!strcmp("spacewidth", tok)) {
549 fscanf(fin, "%d", &fn->spacewid);
550 } else if (!strcmp("special", tok)) {
551 fn->special = 1;
552 } else if (!strcmp("name", tok)) {
553 fscanf(fin, "%s", fn->name);
554 } else if (!strcmp("fontname", tok)) {
555 fscanf(fin, "%s", fn->fontname);
556 } else if (!strcmp("charset", tok)) {
557 while (!font_readchar(fn, fin, &ch_n, &ch_g))
559 break;
561 skipline(fin);
563 for (i = 0; i < ligs_n; i++)
564 font_lig(fn, ligs[i]);
565 fclose(fin);
566 fn->gsub0 = iset_make();
567 fn->gpos0 = iset_make();
568 for (i = 0; i < fn->gsub_n; i++)
569 font_isetinsert(fn, fn->gsub0, i,
570 font_rulefirstpat(fn, &fn->gsub[i]));
571 for (i = 0; i < fn->gpos_n; i++)
572 font_isetinsert(fn, fn->gpos0, i,
573 font_rulefirstpat(fn, &fn->gpos[i]));
574 return fn;
577 void font_close(struct font *fn)
579 int i;
580 for (i = 0; i < fn->gsub_n; i++)
581 free(fn->gsub[i].pats);
582 for (i = 0; i < fn->gpos_n; i++)
583 free(fn->gpos[i].pats);
584 dict_free(fn->gl_dict);
585 dict_free(fn->ch_dict);
586 dict_free(fn->ch_map);
587 iset_free(fn->gsub0);
588 iset_free(fn->gpos0);
589 iset_free(fn->ggrp);
590 free(fn->gsub);
591 free(fn->gpos);
592 free(fn->gl);
593 free(fn);
596 int font_special(struct font *fn)
598 return fn->special;
601 /* return width w for the given font and size */
602 int font_wid(struct font *fn, int sz, int w)
604 sz = font_zoom(fn, sz);
605 return w >= 0 ? DEVWID(sz, w) : -DEVWID(sz, -w);
608 /* return track kerning width for the given size */
609 static int font_twid(struct font *fn, int sz)
611 if (fn->s1 >= 0 && sz <= fn->s1)
612 return fn->n1 * SC_PT;
613 if (fn->s2 >= 0 && sz >= fn->s2)
614 return fn->n2 * SC_PT;
615 if (sz > fn->s1 && sz < fn->s2)
616 return ((sz - fn->s1) * fn->n1 + (fn->s2 - sz) * fn->n2) *
617 (long) SC_PT / (fn->s2 - fn->s1);
618 return 0;
621 /* glyph width, where cfn is the current font and fn is glyph's font */
622 int font_gwid(struct font *fn, struct font *cfn, int sz, int w)
624 struct font *xfn = cfn ? cfn : fn;
625 if (xfn->cs)
626 return xfn->cs * (font_zoom(fn, xfn->cs_ps ? xfn->cs_ps : sz)
627 * SC_IN / 72) / 36;
628 return font_wid(fn, sz, w) + (cfn ? font_twid(fn, sz) : 0) +
629 (font_getbd(xfn) ? font_getbd(xfn) - 1 : 0);
632 /* space width for the give word space or sentence space */
633 int font_swid(struct font *fn, int sz, int ss)
635 return font_gwid(fn, NULL, sz, (fn->spacewid * ss + 6) / 12);
638 int font_getcs(struct font *fn)
640 return fn->cs;
643 void font_setcs(struct font *fn, int cs, int ps)
645 fn->cs = cs;
646 fn->cs_ps = ps;
649 int font_getbd(struct font *fn)
651 return fn->bd;
654 void font_setbd(struct font *fn, int bd)
656 fn->bd = bd;
659 void font_track(struct font *fn, int s1, int n1, int s2, int n2)
661 fn->s1 = s1;
662 fn->n1 = n1;
663 fn->s2 = s2;
664 fn->n2 = n2;
667 int font_zoom(struct font *fn, int sz)
669 return fn->zoom ? (sz * fn->zoom + 500) / 1000 : sz;
672 void font_setzoom(struct font *fn, int zoom)
674 fn->zoom = zoom;
677 /* enable/disable font features; returns the previous value */
678 int font_feat(struct font *fn, char *name, int val)
680 int idx = font_findfeat(fn, name, 0);
681 int old = idx >= 0 ? fn->feat_set[idx] : 0;
682 if (idx >= 0)
683 fn->feat_set[idx] = val != 0;
684 return old;