tr: do not alter the line number in .chop
[neatroff.git] / font.c
blob6b037ebd76ceae004064d08305db0e3750383c5d
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 struct glyph *gl; /* glyphs present in the font */
34 int gl_n, gl_sz; /* number of glyphs in the font */
35 struct dict *gl_dict; /* mapping from gl[i].id to i */
36 struct dict *ch_dict; /* charset mapping */
37 struct dict *ch_map; /* characters mapped via font_map() */
38 /* font features */
39 char feat_name[NFEATS][8]; /* feature names */
40 int feat_set[NFEATS]; /* feature enabled */
41 int feat_n;
42 /* glyph substitution and positioning */
43 struct grule *gsub; /* glyph substitution rules */
44 int gsub_n, gsub_sz;
45 struct grule *gpos; /* glyph positioning rules */
46 int gpos_n, gpos_sz;
47 struct iset *gsub0; /* rules matching a glyph at pos 0 */
48 struct iset *gpos0; /* rules matching a glyph at pos 0 */
49 struct iset *ggrp; /* glyph groups */
52 /* find a glyph by its name */
53 struct glyph *font_find(struct font *fn, char *name)
55 int i = dict_get(fn->ch_map, name);
56 if (i == -1)
57 i = dict_get(fn->ch_dict, name);
58 return i >= 0 ? fn->gl + i : NULL;
61 /* find a glyph by its device-dependent identifier */
62 struct glyph *font_glyph(struct font *fn, char *id)
64 int i = dict_get(fn->gl_dict, id);
65 return i >= 0 ? &fn->gl[i] : NULL;
68 static int font_glyphput(struct font *fn, char *id, char *name, int type)
70 struct glyph *g;
71 if (fn->gl_n == fn->gl_sz) {
72 fn->gl_sz = fn->gl_sz + 1024;
73 fn->gl = mextend(fn->gl, fn->gl_n, fn->gl_sz, sizeof(fn->gl[0]));
75 g = &fn->gl[fn->gl_n];
76 snprintf(g->id, sizeof(g->id), "%s", id);
77 snprintf(g->name, sizeof(g->name), "%s", name);
78 g->type = type;
79 g->font = fn;
80 dict_put(fn->gl_dict, g->id, fn->gl_n);
81 return fn->gl_n++;
84 /* map character name to the given glyph; remove the mapping if id is NULL */
85 int font_map(struct font *fn, char *name, char *id)
87 int gidx = -1;
88 if (id)
89 gidx = font_glyph(fn, id) ? font_glyph(fn, id) - fn->gl : -2;
90 dict_put(fn->ch_map, name, gidx);
91 return 0;
94 /* return nonzero if character name has been mapped with font_map() */
95 int font_mapped(struct font *fn, char *name)
97 return dict_get(fn->ch_map, name) != -1;
100 /* enable/disable ligatures; first bit for liga and the second bit for rlig */
101 static int font_featlg(struct font *fn, int val)
103 int ret = 0;
104 ret |= font_feat(fn, "liga", val & 1);
105 ret |= font_feat(fn, "rlig", val & 2) << 1;
106 return ret;
109 /* enable/disable pairwise kerning */
110 static int font_featkn(struct font *fn, int val)
112 return font_feat(fn, "kern", val);
115 /* glyph index in fn->glyphs[] */
116 static int font_idx(struct font *fn, struct glyph *g)
118 return g ? g - fn->gl : -1;
121 static int font_gpatmatch(struct font *fn, struct gpat *p, int g)
123 int *r;
124 if (!(p->flg & GF_GRP))
125 return p->g == g;
126 r = iset_get(fn->ggrp, p->g);
127 while (r && *r >= 0)
128 if (*r++ == g)
129 return 1;
130 return 0;
133 static int font_rulematch(struct font *fn, struct grule *rule,
134 int *src, int slen, int *dst, int dlen)
136 int sidx = 0; /* the index of matched glyphs in src */
137 int ncon = 0; /* number of initial context glyphs */
138 struct gpat *pats = rule->pats;
139 int j;
140 if (!fn->feat_set[rule->feat])
141 return 0;
142 /* the number of initial context glyphs */
143 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
144 ncon++;
145 if (dlen < ncon)
146 return 0;
147 /* matching the base pattern */
148 for (; j < rule->len; j++) {
149 if (pats[j].flg & GF_REP)
150 continue;
151 if (sidx >= slen || !font_gpatmatch(fn, &pats[j], src[sidx]))
152 return 0;
153 sidx++;
155 /* matching the initial context */
156 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
157 if (!font_gpatmatch(fn, &pats[j], dst[j - ncon]))
158 return 0;
159 return 1;
162 static int font_findrule(struct font *fn, int gsub, int pos,
163 int *fwd, int fwdlen, int *ctx, int ctxlen)
165 struct grule *rules = gsub ? fn->gsub : fn->gpos;
166 int *r1 = iset_get(gsub ? fn->gsub0 : fn->gpos0, fwd[0]);
167 int i = -1;
168 while (r1 && r1[++i] >= 0) {
169 if (r1[i] >= pos && font_rulematch(fn, &rules[r1[i]], fwd,
170 fwdlen, ctx, ctxlen))
171 return r1[i];
173 return -1;
176 /* perform all possible gpos rules on src */
177 static void font_performgpos(struct font *fn, int *src, int slen,
178 int *x, int *y, int *xadv, int *yadv)
180 struct grule *gpos = fn->gpos;
181 int i, k;
182 for (i = 0; i < slen; i++) {
183 int r = font_findrule(fn, 0, 0, src + i, slen - i, src + i, i);
184 if (r >= 0) {
185 struct gpat *pats = gpos[r].pats;
186 /* we should accumulate the values... */
187 for (k = 0; k < gpos[r].len; k++) {
188 x[i + k] = pats[k].x;
189 y[i + k] = pats[k].y;
190 xadv[i + k] = pats[k].xadv;
191 yadv[i + k] = pats[k].yadv;
197 /* find the first gsub rule after pos that matches any glyph in src */
198 static int font_firstgsub(struct font *fn, int pos, int *src, int slen)
200 int best = -1;
201 int i;
202 for (i = 0; i < slen; i++) {
203 int r = font_findrule(fn, 1, pos, src + i, slen - i, src + i, i);
204 if (r >= 0 && (best < 0 || r < best))
205 best = r;
207 return best;
210 /* apply the given gsub rule to all matches in src */
211 static int font_gsubapply(struct font *fn, struct grule *rule,
212 int *src, int slen, int *smap)
214 int dst[WORDLEN];
215 int dlen = 0;
216 int dmap[WORDLEN];
217 int i, j;
218 memset(dmap, 0, slen * sizeof(dmap[i]));
219 for (i = 0; i < slen; i++) {
220 dmap[dlen] = smap[i];
221 if (font_rulematch(fn, rule, src + i, slen - i,
222 dst + dlen, dlen)) {
223 for (j = 0; j < rule->len; j++) {
224 if (rule->pats[j].flg & GF_REP)
225 dst[dlen++] = rule->pats[j].g;
226 if (rule->pats[j].flg & GF_PAT)
227 i++;
229 i--;
230 } else {
231 dst[dlen++] = src[i];
234 memcpy(src, dst, dlen * sizeof(dst[0]));
235 memcpy(smap, dmap, dlen * sizeof(dmap[0]));
236 return dlen;
239 /* perform all possible gsub rules on src */
240 static int font_performgsub(struct font *fn, int *src, int slen, int *smap)
242 int i = -1;
243 while (++i >= 0) {
244 if ((i = font_firstgsub(fn, i, src, slen)) < 0)
245 break;
246 slen = font_gsubapply(fn, &fn->gsub[i], src, slen, smap);
248 return slen;
251 int font_layout(struct font *fn, struct glyph **gsrc, int nsrc, int sz,
252 struct glyph **gdst, int *dmap,
253 int *x, int *y, int *xadv, int *yadv, int lg, int kn)
255 int dst[WORDLEN];
256 int ndst = nsrc;
257 int i;
258 int featlg, featkn;
259 /* initialising dst */
260 for (i = 0; i < nsrc; i++)
261 dst[i] = font_idx(fn, gsrc[i]);
262 for (i = 0; i < ndst; i++)
263 dmap[i] = i;
264 memset(x, 0, ndst * sizeof(x[0]));
265 memset(y, 0, ndst * sizeof(y[0]));
266 memset(xadv, 0, ndst * sizeof(xadv[0]));
267 memset(yadv, 0, ndst * sizeof(yadv[0]));
268 /* substitution rules */
269 if (lg)
270 featlg = font_featlg(fn, 3);
271 ndst = font_performgsub(fn, dst, ndst, dmap);
272 if (lg)
273 font_featlg(fn, featlg);
274 /* positioning rules */
275 if (kn)
276 featkn = font_featkn(fn, 1);
277 font_performgpos(fn, dst, ndst, x, y, xadv, yadv);
278 if (kn)
279 font_featkn(fn, featkn);
280 for (i = 0; i < ndst; i++)
281 gdst[i] = fn->gl + dst[i];
282 return ndst;
285 static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid)
287 struct glyph *g;
288 char tok[ILNLEN];
289 char name[ILNLEN];
290 char id[ILNLEN];
291 int type;
292 if (fscanf(fin, "%s %s", name, tok) != 2)
293 return 1;
294 if (!strcmp("---", name))
295 sprintf(name, "c%04d", *n);
296 if (strcmp("\"", tok)) {
297 if (fscanf(fin, "%d %s", &type, id) != 2)
298 return 1;
299 *gid = dict_get(fn->gl_dict, id);
300 if (*gid < 0) {
301 *gid = font_glyphput(fn, id, name, type);
302 g = &fn->gl[*gid];
303 sscanf(tok, "%hd,%hd,%hd,%hd,%hd", &g->wid,
304 &g->llx, &g->lly, &g->urx, &g->ury);
307 dict_put(fn->ch_dict, name, *gid);
308 (*n)++;
309 return 0;
312 static int font_findfeat(struct font *fn, char *feat, int mk)
314 int i;
315 for (i = 0; i < fn->feat_n; i++)
316 if (!strcmp(feat, fn->feat_name[i]))
317 return i;
318 if (mk) {
319 snprintf(fn->feat_name[fn->feat_n],
320 sizeof(fn->feat_name[fn->feat_n]), "%s", feat);
322 return mk ? fn->feat_n++ : -1;
325 static struct gpat *font_gpat(struct font *fn, int len)
327 struct gpat *pats = xmalloc(len * sizeof(pats[0]));
328 memset(pats, 0, len * sizeof(pats[0]));
329 return pats;
332 static struct grule *font_gsub(struct font *fn, char *feat, int len)
334 struct grule *rule;
335 struct gpat *pats = font_gpat(fn, len);
336 if (fn->gsub_n == fn->gsub_sz) {
337 fn->gsub_sz = fn->gsub_sz + 1024;
338 fn->gsub = mextend(fn->gsub, fn->gsub_n, fn->gsub_sz,
339 sizeof(fn->gsub[0]));
341 rule = &fn->gsub[fn->gsub_n++];
342 rule->pats = pats;
343 rule->len = len;
344 rule->feat = font_findfeat(fn, feat, 1);
345 return rule;
348 static struct grule *font_gpos(struct font *fn, char *feat, int len)
350 struct grule *rule;
351 struct gpat *pats = font_gpat(fn, len);
352 if (fn->gpos_n == fn->gpos_sz) {
353 fn->gpos_sz = fn->gpos_sz + 1024;
354 fn->gpos = mextend(fn->gpos, fn->gpos_n, fn->gpos_sz,
355 sizeof(fn->gpos[0]));
357 rule = &fn->gpos[fn->gpos_n++];
358 rule->pats = pats;
359 rule->len = len;
360 rule->feat = font_findfeat(fn, feat, 1);
361 return rule;
364 static int font_readgpat(struct font *fn, struct gpat *p, char *s)
366 if (s[0] == '@') {
367 p->g = atoi(s + 1);
368 if (iset_len(fn->ggrp, p->g) == 1)
369 p->g = iset_get(fn->ggrp, p->g)[0];
370 else
371 p->flg |= GF_GRP;
372 } else {
373 p->g = font_idx(fn, font_glyph(fn, s));
375 return p->g < 0;
378 static int font_readgsub(struct font *fn, FILE *fin)
380 char tok[128];
381 struct grule *rule;
382 int i, n;
383 if (fscanf(fin, "%s %d", tok, &n) != 2)
384 return 1;
385 rule = font_gsub(fn, tok, n);
386 for (i = 0; i < n; i++) {
387 if (fscanf(fin, "%s", tok) != 1)
388 return 1;
389 if (tok[0] == '-')
390 rule->pats[i].flg = GF_PAT;
391 if (tok[0] == '=')
392 rule->pats[i].flg = GF_CON;
393 if (tok[0] == '+')
394 rule->pats[i].flg = GF_REP;
395 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok + 1))
396 return 0;
398 return 0;
401 static int font_readgpos(struct font *fn, FILE *fin)
403 char tok[128];
404 char *col;
405 struct grule *rule;
406 int i, n;
407 if (fscanf(fin, "%s %d", tok, &n) != 2)
408 return 1;
409 rule = font_gpos(fn, tok, n);
410 for (i = 0; i < n; i++) {
411 if (fscanf(fin, "%s", tok) != 1)
412 return 1;
413 col = strchr(tok, ':');
414 if (col)
415 *col = '\0';
416 rule->pats[i].flg = GF_PAT;
417 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok))
418 return 0;
419 if (col)
420 sscanf(col + 1, "%hd%hd%hd%hd",
421 &rule->pats[i].x, &rule->pats[i].y,
422 &rule->pats[i].xadv, &rule->pats[i].yadv);
424 return 0;
427 static int font_readggrp(struct font *fn, FILE *fin)
429 char tok[ILNLEN];
430 int id, n, i, g;
431 if (fscanf(fin, "%d %d", &id, &n) != 2)
432 return 1;
433 for (i = 0; i < n; i++) {
434 if (fscanf(fin, "%s", tok) != 1)
435 return 1;
436 g = font_idx(fn, font_glyph(fn, tok));
437 if (g >= 0)
438 iset_put(fn->ggrp, id, g);
440 return 0;
443 static int font_readkern(struct font *fn, FILE *fin)
445 char c1[ILNLEN], c2[ILNLEN];
446 struct grule *rule;
447 int val;
448 if (fscanf(fin, "%s %s %d", c1, c2, &val) != 3)
449 return 1;
450 rule = font_gpos(fn, "kern", 2);
451 rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
452 rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
453 rule->pats[0].xadv = val;
454 rule->pats[0].flg = GF_PAT;
455 rule->pats[1].flg = GF_PAT;
456 return 0;
459 static void font_lig(struct font *fn, char *lig)
461 char c[GNLEN];
462 int g[WORDLEN];
463 struct grule *rule;
464 char *s = lig;
465 int j, n = 0;
466 while (utf8read(&s, c) > 0)
467 g[n++] = font_idx(fn, font_find(fn, c));
468 rule = font_gsub(fn, "liga", n + 1);
469 for (j = 0; j < n; j++) {
470 rule->pats[j].g = g[j];
471 rule->pats[j].flg = GF_PAT;
473 rule->pats[n].g = font_idx(fn, font_find(fn, lig));
474 rule->pats[n].flg = GF_REP;
477 static void skipline(FILE* filp)
479 int c;
480 do {
481 c = getc(filp);
482 } while (c != '\n' && c != EOF);
485 static struct gpat *font_rulefirstpat(struct font *fn, struct grule *rule)
487 int i;
488 for (i = 0; i < rule->len; i++)
489 if (!(rule->pats[i].flg & (GF_REP | GF_CON)))
490 return &rule->pats[i];
491 return NULL;
494 static void font_isetinsert(struct font *fn, struct iset *iset, int rule, struct gpat *p)
496 if (p->flg & GF_GRP) {
497 int *r = iset_get(fn->ggrp, p->g);
498 while (r && *r >= 0)
499 iset_put(iset, *r++, rule);
500 } else {
501 if (p->g >= 0)
502 iset_put(iset, p->g, rule);
506 struct font *font_open(char *path)
508 struct font *fn;
509 int ch_g = -1; /* last glyph in the charset */
510 int ch_n = 0; /* number of glyphs in the charset */
511 char tok[ILNLEN];
512 FILE *fin;
513 char ligs[512][GNLEN];
514 int ligs_n = 0;
515 int i;
516 fin = fopen(path, "r");
517 if (!fin)
518 return NULL;
519 fn = xmalloc(sizeof(*fn));
520 if (!fn) {
521 fclose(fin);
522 return NULL;
524 memset(fn, 0, sizeof(*fn));
525 fn->gl_dict = dict_make(-1, 1, 0);
526 fn->ch_dict = dict_make(-1, 1, 0);
527 fn->ch_map = dict_make(-1, 1, 0);
528 fn->ggrp = iset_make();
529 while (fscanf(fin, "%s", tok) == 1) {
530 if (!strcmp("char", tok)) {
531 font_readchar(fn, fin, &ch_n, &ch_g);
532 } else if (!strcmp("kern", tok)) {
533 font_readkern(fn, fin);
534 } else if (!strcmp("ligatures", tok)) {
535 while (fscanf(fin, "%s", ligs[ligs_n]) == 1) {
536 if (!strcmp("0", ligs[ligs_n]))
537 break;
538 if (ligs_n < LEN(ligs))
539 ligs_n++;
541 } else if (!strcmp("gsub", tok)) {
542 font_readgsub(fn, fin);
543 } else if (!strcmp("gpos", tok)) {
544 font_readgpos(fn, fin);
545 } else if (!strcmp("ggrp", tok)) {
546 font_readggrp(fn, fin);
547 } else if (!strcmp("spacewidth", tok)) {
548 fscanf(fin, "%d", &fn->spacewid);
549 } else if (!strcmp("special", tok)) {
550 fn->special = 1;
551 } else if (!strcmp("name", tok)) {
552 fscanf(fin, "%s", fn->name);
553 } else if (!strcmp("fontname", tok)) {
554 fscanf(fin, "%s", fn->fontname);
555 } else if (!strcmp("charset", tok)) {
556 while (!font_readchar(fn, fin, &ch_n, &ch_g))
558 break;
560 skipline(fin);
562 for (i = 0; i < ligs_n; i++)
563 font_lig(fn, ligs[i]);
564 fclose(fin);
565 fn->gsub0 = iset_make();
566 fn->gpos0 = iset_make();
567 for (i = 0; i < fn->gsub_n; i++)
568 font_isetinsert(fn, fn->gsub0, i,
569 font_rulefirstpat(fn, &fn->gsub[i]));
570 for (i = 0; i < fn->gpos_n; i++)
571 font_isetinsert(fn, fn->gpos0, i,
572 font_rulefirstpat(fn, &fn->gpos[i]));
573 return fn;
576 void font_close(struct font *fn)
578 int i;
579 for (i = 0; i < fn->gsub_n; i++)
580 free(fn->gsub[i].pats);
581 for (i = 0; i < fn->gpos_n; i++)
582 free(fn->gpos[i].pats);
583 dict_free(fn->gl_dict);
584 dict_free(fn->ch_dict);
585 dict_free(fn->ch_map);
586 iset_free(fn->gsub0);
587 iset_free(fn->gpos0);
588 iset_free(fn->ggrp);
589 free(fn->gsub);
590 free(fn->gpos);
591 free(fn->gl);
592 free(fn);
595 int font_special(struct font *fn)
597 return fn->special;
600 /* return width w for the given font and size */
601 int font_wid(struct font *fn, int sz, int w)
603 sz = font_zoom(fn, sz);
604 return w >= 0 ? DEVWID(sz, w) : -DEVWID(sz, -w);
607 /* glyph width, where cfn is the current font and fn is glyph's font */
608 int font_gwid(struct font *fn, struct font *cfn, int sz, int w)
610 if (cfn->cs)
611 return cfn->cs * (font_zoom(fn, cfn->cs_ps ? cfn->cs_ps : sz)
612 * SC_IN / 72) / 36;
613 return font_wid(fn, sz, w) + (font_getbd(cfn) ? font_getbd(cfn) - 1 : 0);
616 /* space width for the give word space or sentence space */
617 int font_swid(struct font *fn, int sz, int ss)
619 return font_gwid(fn, fn, sz, (fn->spacewid * ss + 6) / 12);
622 int font_getcs(struct font *fn)
624 return fn->cs;
627 void font_setcs(struct font *fn, int cs, int ps)
629 fn->cs = cs;
630 fn->cs_ps = ps;
633 int font_getbd(struct font *fn)
635 return fn->bd;
638 void font_setbd(struct font *fn, int bd)
640 fn->bd = bd;
643 int font_zoom(struct font *fn, int sz)
645 return fn->zoom ? (sz * fn->zoom + 500) / 1000 : sz;
648 void font_setzoom(struct font *fn, int zoom)
650 fn->zoom = zoom;
653 /* enable/disable font features; returns the previous value */
654 int font_feat(struct font *fn, char *name, int val)
656 int idx = font_findfeat(fn, name, 0);
657 int old = idx >= 0 ? fn->feat_set[idx] : 0;
658 if (idx >= 0)
659 fn->feat_set[idx] = val != 0;
660 return old;