tr: escaped spaces inside macro arguments
[neatroff.git] / font.c
blobeb305e1dc8b86e6a26a099a8ddba3684e51ad434
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)
9 #define GHASH(g1, g2) ((((g2) + 1) << 16) | ((g1) + 1))
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 pattern for gsub and gpos tables; each grule has some gpats */
17 struct gpat {
18 short g; /* glyph index */
19 short flg; /* pattern flags; GF_* */
20 short x, y, xadv, yadv; /* gpos data */
23 /* glyph substitution and positioning rules */
24 struct grule {
25 struct gpat *pats;
26 short len; /* pats[] length */
27 short feat; /* feature owning this rule */
28 short pos; /* position of this rule in the file */
29 int hash; /* hash of this rule for sorting and comparison */
32 struct font {
33 char name[FNLEN];
34 char fontname[FNLEN];
35 int spacewid;
36 int special;
37 int cs, cs_ps, bd, zoom; /* for .cs, .bd, .fzoom requests */
38 struct glyph gl[NGLYPHS]; /* glyphs present in the font */
39 int gl_n; /* number of glyphs in the font */
40 struct dict gl_dict; /* mapping from gl[i].id to i */
41 struct dict ch_dict; /* charset mapping */
42 struct dict ch_map; /* characters mapped via font_map() */
43 /* font features */
44 char feat_name[NFEATS][8]; /* feature names */
45 int feat_set[NFEATS]; /* feature enabled */
46 int feat_n;
47 /* glyph substitution and positioning */
48 struct grule gsub[NGRULES]; /* glyph substitution rules */
49 int gsub_n;
50 struct grule gpos[NGRULES]; /* glyph positioning rules */
51 int gpos_n;
52 int *ggrp[NGRULES]; /* glyph groups */
53 int ggrp_len[NGRULES];
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)
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 struct glyph *font_glyphput(struct font *fn, char *id, char *name, int type)
74 int i = fn->gl_n++;
75 struct glyph *g;
76 g = &fn->gl[i];
77 strcpy(g->id, id);
78 strcpy(g->name, name);
79 g->type = type;
80 g->font = fn;
81 dict_put(&fn->gl_dict, g->id, i);
82 return g;
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 /* compare their hashes, then their positions to make qsort() stable */
123 static int grulecmp(void *v1, void *v2)
125 struct grule *r1 = v1;
126 struct grule *r2 = v2;
127 return r1->hash == r2->hash ? r1->pos - r2->pos : r1->hash - r2->hash;
130 /* the hashing function for grule structs, based on their first two glyphs */
131 static int grule_hash(struct grule *rule)
133 int g1 = -1, g2 = -1;
134 int i = 0;
135 /* finding the first glyph; -1 if FG_GRP */
136 while (i < rule->len && rule->pats[i].flg & (GF_REP | GF_CON))
137 i++; /* skipping replacement and context glyphs */
138 if (i < rule->len && rule->pats[i].flg == GF_PAT)
139 g1 = rule->pats[i].g;
140 i++;
141 /* finding the second glyph; -1 if FG_GRP */
142 while (i < rule->len && rule->pats[i].flg & GF_REP)
143 i++; /* skipping replacement glyphs */
144 if (i < rule->len && rule->pats[i].flg == GF_PAT)
145 g2 = rule->pats[i].g;
146 return GHASH(g1, g2);
149 static int grule_find(struct grule *rules, int n, int hash)
151 int l = 0;
152 int h = n;
153 while (l < h) {
154 int m = (l + h) >> 1;
155 if (rules[m].hash >= hash)
156 h = m;
157 else
158 l = m + 1;
160 return l;
163 static int font_gpatmatch(struct font *fn, struct gpat *p, int g)
165 int i;
166 if (!(p->flg & GF_GRP))
167 return p->g == g;
168 for (i = 0; i < fn->ggrp_len[p->g]; i++)
169 if (fn->ggrp[p->g][i] == g)
170 return 1;
171 return 0;
174 static int font_rulematch(struct font *fn, struct grule *rule,
175 int *src, int slen, int *dst, int dlen)
177 int sidx = 0; /* the index of matched glyphs in src */
178 int ncon = 0; /* number of initial context glyphs */
179 struct gpat *pats = rule->pats;
180 int j;
181 if (!fn->feat_set[rule->feat])
182 return 0;
183 /* the number of initial context glyphs */
184 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
185 ncon++;
186 if (dlen < ncon)
187 return 0;
188 /* matching the base pattern */
189 for (; j < rule->len; j++) {
190 if (pats[j].flg & GF_REP)
191 continue;
192 if (sidx >= slen || !font_gpatmatch(fn, &pats[j], src[sidx]))
193 return 0;
194 sidx++;
196 /* matching the initial context */
197 for (j = 0; j < rule->len && pats[j].flg & GF_CON; j++)
198 if (!font_gpatmatch(fn, &pats[j], dst[j - ncon]))
199 return 0;
200 return 1;
203 /* perform all possible gpos rules on src */
204 static void font_performgpos(struct font *fn, int *src, int slen,
205 int *x, int *y, int *xadv, int *yadv)
207 struct grule *gpos = fn->gpos;
208 int n = fn->gpos_n;
209 int i, j, k;
210 for (i = 0; i < slen; i++) {
211 /* possible hash values for matching gpos rules at src + slen */
212 for (j = 0; j < 4 && j < (slen << 1); j++) {
213 int hash = GHASH(j & 1 ? src[i] : -1, j & 2 ? src[i + 1] : -1);
214 int idx = grule_find(gpos, n, hash);
215 while (idx < n && gpos[idx].hash == hash) {
216 if (font_rulematch(fn, &gpos[idx],
217 src + i, slen - i, src + i, i)) {
218 struct gpat *pats = gpos[idx].pats;
219 /* we should accumulate the values... */
220 for (k = 0; k < gpos[idx].len; k++) {
221 x[i + k] = pats[k].x;
222 y[i + k] = pats[k].y;
223 xadv[i + k] = pats[k].xadv;
224 yadv[i + k] = pats[k].yadv;
227 idx++;
233 /* find the first gsub rule after pos that matches any glyph in src */
234 static struct grule *font_firstgsub(struct font *fn, int pos, int *src, int slen)
236 struct grule *rules = fn->gsub;
237 int n = fn->gsub_n;
238 struct grule *best = NULL;
239 int i, j;
240 for (i = 0; i < slen; i++) {
241 /* possible hash values for matching gsub rules at src + slen */
242 for (j = 0; j < 2 && i + j < slen; j++) {
243 int hash = GHASH(src[i], j ? src[i + 1] : -1);
244 int idx = grule_find(rules, n, hash);
245 while (idx < n && rules[idx].hash == hash &&
246 (!best || rules[idx].pos < best->pos)) {
247 if (rules[idx].pos >= pos)
248 if (font_rulematch(fn, &rules[idx],
249 src + i, slen - i, src + i, i))
250 best = &rules[idx];
251 idx++;
255 return best;
258 /* apply the given gsub rule to all matches in src */
259 static int font_gsubapply(struct font *fn, struct grule *rule,
260 int *src, int slen, int *smap)
262 int dst[WORDLEN];
263 int dlen = 0;
264 int dmap[WORDLEN];
265 int i, j;
266 memset(dmap, 0, slen * sizeof(dmap[i]));
267 for (i = 0; i < slen; i++) {
268 int hash1 = GHASH(src[i], -1);
269 int hash2 = GHASH(src[i], i + 1 < slen ? src[i + 1] : -1);
270 int hmatch = rule->hash == hash1 || rule->hash == hash2;
271 dmap[dlen] = smap[i];
272 if (hmatch && font_rulematch(fn, rule, src + i,
273 slen - i, dst + dlen, dlen)) {
274 for (j = 0; j < rule->len; j++) {
275 if (rule->pats[j].flg & GF_REP)
276 dst[dlen++] = rule->pats[j].g;
277 if (rule->pats[j].flg & GF_PAT)
278 i++;
280 i--;
281 } else {
282 dst[dlen++] = src[i];
285 memcpy(src, dst, dlen * sizeof(dst[0]));
286 memcpy(smap, dmap, dlen * sizeof(dmap[0]));
287 return dlen;
290 /* perform all possible gsub rules on src */
291 static int font_performgsub(struct font *fn, int *src, int slen, int *smap)
293 int i = 0;
294 while (i >= 0) {
295 struct grule *rule = font_firstgsub(fn, i, src, slen);
296 if (rule)
297 slen = font_gsubapply(fn, rule, src, slen, smap);
298 i = rule ? rule->pos + 1 : -1;
300 return slen;
303 int font_layout(struct font *fn, struct glyph **gsrc, int nsrc, int sz,
304 struct glyph **gdst, int *dmap,
305 int *x, int *y, int *xadv, int *yadv, int lg, int kn)
307 int dst[WORDLEN];
308 int ndst = nsrc;
309 int i;
310 int featlg, featkn;
311 /* initialising dst */
312 for (i = 0; i < nsrc; i++)
313 dst[i] = font_idx(fn, gsrc[i]);
314 for (i = 0; i < ndst; i++)
315 dmap[i] = i;
316 memset(x, 0, ndst * sizeof(x[0]));
317 memset(y, 0, ndst * sizeof(y[0]));
318 memset(xadv, 0, ndst * sizeof(xadv[0]));
319 memset(yadv, 0, ndst * sizeof(yadv[0]));
320 /* substitution rules */
321 if (lg)
322 featlg = font_featlg(fn, 3);
323 ndst = font_performgsub(fn, dst, ndst, dmap);
324 if (lg)
325 font_featlg(fn, featlg);
326 /* positioning rules */
327 if (kn)
328 featkn = font_featkn(fn, 1);
329 font_performgpos(fn, dst, ndst, x, y, xadv, yadv);
330 if (kn)
331 font_featkn(fn, featkn);
332 for (i = 0; i < ndst; i++)
333 gdst[i] = fn->gl + dst[i];
334 return ndst;
337 static int font_readchar(struct font *fn, FILE *fin, int *n, struct glyph **g)
339 char tok[ILNLEN];
340 char name[ILNLEN];
341 char id[ILNLEN];
342 int type;
343 if (*n + 1 == NGLYPHS)
344 errmsg("neatroff: NGLYPHS too low\n");
345 if (*n >= NGLYPHS)
346 return 0;
347 if (fscanf(fin, "%s %s", name, tok) != 2)
348 return 1;
349 if (!strcmp("---", name))
350 sprintf(name, "c%04d", *n);
351 if (strcmp("\"", tok)) {
352 if (fscanf(fin, "%d %s", &type, id) != 2)
353 return 1;
354 *g = font_glyph(fn, id);
355 if (!*g) {
356 *g = font_glyphput(fn, id, name, type);
357 sscanf(tok, "%hd,%hd,%hd,%hd,%hd", &(*g)->wid,
358 &(*g)->llx, &(*g)->lly, &(*g)->urx, &(*g)->ury);
361 dict_put(&fn->ch_dict, name, *g - fn->gl);
362 (*n)++;
363 return 0;
366 static int font_findfeat(struct font *fn, char *feat, int mk)
368 int i;
369 for (i = 0; i < fn->feat_n; i++)
370 if (!strcmp(feat, fn->feat_name[i]))
371 return i;
372 if (mk)
373 strcpy(fn->feat_name[fn->feat_n], feat);
374 return mk ? fn->feat_n++ : -1;
377 static struct gpat *font_gpat(struct font *fn, int len)
379 struct gpat *pats = xmalloc(len * sizeof(pats[0]));
380 memset(pats, 0, len * sizeof(pats[0]));
381 return pats;
384 static struct grule *font_gsub(struct font *fn, char *feat, int len)
386 struct grule *rule;
387 struct gpat *pats = font_gpat(fn, len);
388 if (fn->gsub_n + 1 == LEN(fn->gsub))
389 errmsg("neatroff: NGRULES too low\n");
390 if (fn->gsub_n >= LEN(fn->gsub) || !pats)
391 return NULL;
392 rule = &fn->gsub[fn->gsub_n++];
393 rule->pats = pats;
394 rule->len = len;
395 rule->feat = font_findfeat(fn, feat, 1);
396 return rule;
399 static struct grule *font_gpos(struct font *fn, char *feat, int len)
401 struct grule *rule;
402 struct gpat *pats = font_gpat(fn, len);
403 if (fn->gpos_n + 1 == LEN(fn->gpos))
404 errmsg("neatroff: NGRULES too low\n");
405 if (fn->gpos_n >= LEN(fn->gpos) || !pats)
406 return NULL;
407 rule = &fn->gpos[fn->gpos_n++];
408 rule->pats = pats;
409 rule->len = len;
410 rule->feat = font_findfeat(fn, feat, 1);
411 return rule;
414 static int font_readgpat(struct font *fn, struct gpat *p, char *s)
416 if (s[0] == '@') {
417 p->g = atoi(s + 1);
418 if (fn->ggrp_len[p->g] == 1)
419 p->g = fn->ggrp[p->g][0];
420 else
421 p->flg |= GF_GRP;
422 } else {
423 p->g = font_idx(fn, font_glyph(fn, s));
425 return p->g < 0;
428 static int font_readgsub(struct font *fn, FILE *fin)
430 char tok[128];
431 struct grule *rule;
432 int i, n;
433 if (fscanf(fin, "%s %d", tok, &n) != 2)
434 return 1;
435 if (!(rule = font_gsub(fn, tok, n)))
436 return 0;
437 for (i = 0; i < n; i++) {
438 if (fscanf(fin, "%s", tok) != 1)
439 return 1;
440 if (tok[0] == '-')
441 rule->pats[i].flg = GF_PAT;
442 if (tok[0] == '=')
443 rule->pats[i].flg = GF_CON;
444 if (tok[0] == '+')
445 rule->pats[i].flg = GF_REP;
446 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok + 1))
447 return 0;
449 return 0;
452 static int font_readgpos(struct font *fn, FILE *fin)
454 char tok[128];
455 char *col;
456 struct grule *rule;
457 int i, n;
458 if (fscanf(fin, "%s %d", tok, &n) != 2)
459 return 1;
460 if (!(rule = font_gpos(fn, tok, n)))
461 return 0;
462 for (i = 0; i < n; i++) {
463 if (fscanf(fin, "%s", tok) != 1)
464 return 1;
465 col = strchr(tok, ':');
466 if (col)
467 *col = '\0';
468 rule->pats[i].flg = GF_PAT;
469 if (!tok[0] || font_readgpat(fn, &rule->pats[i], tok))
470 return 0;
471 if (col)
472 sscanf(col + 1, "%hd%hd%hd%hd",
473 &rule->pats[i].x, &rule->pats[i].y,
474 &rule->pats[i].xadv, &rule->pats[i].yadv);
476 return 0;
479 static int font_readggrp(struct font *fn, FILE *fin)
481 char tok[ILNLEN];
482 int id, n, i, g;
483 if (fscanf(fin, "%d %d", &id, &n) != 2)
484 return 1;
485 if (id >= LEN(fn->ggrp)) {
486 errmsg("neatroff: NGRULES too low\n");
487 return 0;
489 if (fn->ggrp[id])
490 free(fn->ggrp[id]);
491 fn->ggrp[id] = xmalloc(n * sizeof(fn->ggrp[id][0]));
492 fn->ggrp_len[id] = 0;
493 for (i = 0; i < n; i++) {
494 if (fscanf(fin, "%s", tok) != 1)
495 return 1;
496 g = font_idx(fn, font_glyph(fn, tok));
497 if (g >= 0)
498 fn->ggrp[id][fn->ggrp_len[id]++] = g;
500 return 0;
503 static int font_readkern(struct font *fn, FILE *fin)
505 char c1[ILNLEN], c2[ILNLEN];
506 struct grule *rule;
507 int val;
508 if (fscanf(fin, "%s %s %d", c1, c2, &val) != 3)
509 return 1;
510 if (!(rule = font_gpos(fn, "kern", 2)))
511 return 0;
512 rule->pats[0].g = font_idx(fn, font_glyph(fn, c1));
513 rule->pats[1].g = font_idx(fn, font_glyph(fn, c2));
514 rule->pats[0].xadv = val;
515 rule->pats[0].flg = GF_PAT;
516 rule->pats[1].flg = GF_PAT;
517 return 0;
520 static void font_lig(struct font *fn, char *lig)
522 char c[GNLEN];
523 int g[WORDLEN];
524 struct grule *rule;
525 char *s = lig;
526 int j, n = 0;
527 while (utf8read(&s, c) > 0)
528 g[n++] = font_idx(fn, font_find(fn, c));
529 if (!(rule = font_gsub(fn, "liga", n + 1)))
530 return;
531 for (j = 0; j < n; j++) {
532 rule->pats[j].g = g[j];
533 rule->pats[j].flg = GF_PAT;
535 rule->pats[n].g = font_idx(fn, font_find(fn, lig));
536 rule->pats[n].flg = GF_REP;
539 static void skipline(FILE* filp)
541 int c;
542 do {
543 c = getc(filp);
544 } while (c != '\n' && c != EOF);
547 struct font *font_open(char *path)
549 struct font *fn;
550 struct glyph *ch_g = NULL; /* last glyph in the charset */
551 int ch_n = 0; /* number of glyphs in the charset */
552 char tok[ILNLEN];
553 FILE *fin;
554 char ligs[512][GNLEN];
555 int ligs_n = 0;
556 int i;
557 fin = fopen(path, "r");
558 if (!fin)
559 return NULL;
560 fn = xmalloc(sizeof(*fn));
561 if (!fn) {
562 fclose(fin);
563 return NULL;
565 memset(fn, 0, sizeof(*fn));
566 dict_init(&fn->gl_dict, NGLYPHS, -1, 0, 0);
567 dict_init(&fn->ch_dict, NGLYPHS, -1, 1, 0);
568 dict_init(&fn->ch_map, NGLYPHS, -1, 1, 0);
569 while (fscanf(fin, "%s", tok) == 1) {
570 if (!strcmp("char", tok)) {
571 font_readchar(fn, fin, &ch_n, &ch_g);
572 } else if (!strcmp("kern", tok)) {
573 font_readkern(fn, fin);
574 } else if (!strcmp("ligatures", tok)) {
575 while (fscanf(fin, "%s", ligs[ligs_n]) == 1) {
576 if (!strcmp("0", ligs[ligs_n]))
577 break;
578 if (ligs_n < LEN(ligs))
579 ligs_n++;
581 } else if (!strcmp("gsub", tok)) {
582 font_readgsub(fn, fin);
583 } else if (!strcmp("gpos", tok)) {
584 font_readgpos(fn, fin);
585 } else if (!strcmp("ggrp", tok)) {
586 font_readggrp(fn, fin);
587 } else if (!strcmp("spacewidth", tok)) {
588 fscanf(fin, "%d", &fn->spacewid);
589 } else if (!strcmp("special", tok)) {
590 fn->special = 1;
591 } else if (!strcmp("name", tok)) {
592 fscanf(fin, "%s", fn->name);
593 } else if (!strcmp("fontname", tok)) {
594 fscanf(fin, "%s", fn->fontname);
595 } else if (!strcmp("charset", tok)) {
596 while (!font_readchar(fn, fin, &ch_n, &ch_g))
598 break;
600 skipline(fin);
602 for (i = 0; i < ligs_n; i++)
603 font_lig(fn, ligs[i]);
604 fclose(fin);
605 for (i = 0; i < fn->gsub_n; i++)
606 fn->gsub[i].pos = i;
607 for (i = 0; i < fn->gpos_n; i++)
608 fn->gpos[i].pos = i;
609 for (i = 0; i < fn->gsub_n; i++)
610 fn->gsub[i].hash = grule_hash(&fn->gsub[i]);
611 for (i = 0; i < fn->gpos_n; i++)
612 fn->gpos[i].hash = grule_hash(&fn->gpos[i]);
613 qsort(fn->gsub, fn->gsub_n, sizeof(fn->gsub[0]), (void *) grulecmp);
614 qsort(fn->gpos, fn->gpos_n, sizeof(fn->gpos[0]), (void *) grulecmp);
615 return fn;
618 void font_close(struct font *fn)
620 int i;
621 for (i = 0; i < fn->gsub_n; i++)
622 free(fn->gsub[i].pats);
623 for (i = 0; i < fn->gpos_n; i++)
624 free(fn->gpos[i].pats);
625 for (i = 0; i < LEN(fn->ggrp); i++)
626 free(fn->ggrp[i]);
627 dict_done(&fn->gl_dict);
628 dict_done(&fn->ch_dict);
629 dict_done(&fn->ch_map);
630 free(fn);
633 int font_special(struct font *fn)
635 return fn->special;
638 /* return width w for the given font and size */
639 int font_wid(struct font *fn, int sz, int w)
641 sz = font_zoom(fn, sz);
642 return w >= 0 ? DEVWID(sz, w) : -DEVWID(sz, -w);
645 /* glyph width, where cfn is the current font and fn is glyph's font */
646 int font_gwid(struct font *fn, struct font *cfn, int sz, int w)
648 if (cfn->cs)
649 return cfn->cs * (font_zoom(fn, cfn->cs_ps ? cfn->cs_ps : sz)
650 * SC_IN / 72) / 36;
651 return font_wid(fn, sz, w) + (font_getbd(cfn) ? font_getbd(cfn) - 1 : 0);
654 /* space width for the give word space or sentence space */
655 int font_swid(struct font *fn, int sz, int ss)
657 return font_gwid(fn, fn, sz, (fn->spacewid * ss + 6) / 12);
660 int font_getcs(struct font *fn)
662 return fn->cs;
665 void font_setcs(struct font *fn, int cs, int ps)
667 fn->cs = cs;
668 fn->cs_ps = ps;
671 int font_getbd(struct font *fn)
673 return fn->bd;
676 void font_setbd(struct font *fn, int bd)
678 fn->bd = bd;
681 int font_zoom(struct font *fn, int sz)
683 return fn->zoom ? (sz * fn->zoom + 500) / 1000 : sz;
686 void font_setzoom(struct font *fn, int zoom)
688 fn->zoom = zoom;
691 /* enable/disable font features; returns the previous value */
692 int font_feat(struct font *fn, char *name, int val)
694 int idx = font_findfeat(fn, name, 0);
695 int old = idx >= 0 ? fn->feat_set[idx] : 0;
696 if (idx >= 0)
697 fn->feat_set[idx] = val != 0;
698 return old;