wb: disable ligatures and pairwise kerning while changing fonts
[neatroff.git] / wb.c
blobfb5cf9618cf55e076a17fac0a00fc41bbbb441c5
1 /* word buffer */
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "roff.h"
7 #define R_F(wb) ((wb)->r_f >= 0 ? (wb)->r_f : n_f) /* current font */
8 #define R_S(wb) ((wb)->r_s >= 0 ? (wb)->r_s : n_s) /* current size */
9 #define R_M(wb) ((wb)->r_m >= 0 ? (wb)->r_m : n_m) /* current color */
11 void wb_init(struct wb *wb)
13 memset(wb, 0, sizeof(*wb));
14 sbuf_init(&wb->sbuf);
15 wb->f = -1;
16 wb->s = -1;
17 wb->m = -1;
18 wb->r_f = -1;
19 wb->r_s = -1;
20 wb->r_m = -1;
23 void wb_done(struct wb *wb)
25 sbuf_done(&wb->sbuf);
28 /* update wb->st and wb->sb */
29 static void wb_stsb(struct wb *wb)
31 wb->st = MIN(wb->st, wb->v - SC_HT);
32 wb->sb = MAX(wb->sb, wb->v);
35 /* append font and size to the buffer if needed */
36 static void wb_font(struct wb *wb)
38 if (wb->f != R_F(wb)) {
39 sbuf_printf(&wb->sbuf, "%cf(%02d", c_ec, R_F(wb));
40 wb->f = R_F(wb);
42 if (wb->s != R_S(wb)) {
43 sbuf_printf(&wb->sbuf, "%cs(%02d", c_ec, R_S(wb));
44 wb->s = R_S(wb);
46 if (!n_cp && wb->m != R_M(wb)) {
47 sbuf_printf(&wb->sbuf, "%cm[%s]", c_ec, clr_str(R_M(wb)));
48 wb->m = R_M(wb);
50 wb_stsb(wb);
53 void wb_hmov(struct wb *wb, int n)
55 wb->h += n;
56 sbuf_printf(&wb->sbuf, "%ch'%du'", c_ec, n);
59 void wb_vmov(struct wb *wb, int n)
61 wb->v += n;
62 sbuf_printf(&wb->sbuf, "%cv'%du'", c_ec, n);
65 void wb_els(struct wb *wb, int els)
67 if (els > wb->els_pos)
68 wb->els_pos = els;
69 if (els < wb->els_neg)
70 wb->els_neg = els;
71 sbuf_printf(&wb->sbuf, "%cx'%du'", c_ec, els);
74 void wb_etc(struct wb *wb, char *x)
76 wb_font(wb);
77 sbuf_printf(&wb->sbuf, "%cX\x02%s\x02", c_ec, x);
80 /* make sure nothing is appended to wb after the last wb_put() */
81 static void wb_prevcheck(struct wb *wb)
83 if (wb->prev_ll != sbuf_len(&wb->sbuf))
84 wb->prev_n = 0;
87 /* mark wb->prev_c[] as valid */
88 static void wb_prevok(struct wb *wb)
90 wb->prev_ll = sbuf_len(&wb->sbuf);
93 /* append c to wb->prev_c[] */
94 static void wb_prevput(struct wb *wb, char *c, int ll)
96 if (wb->prev_n == LEN(wb->prev_c))
97 wb->prev_n--;
98 memmove(wb->prev_l + 1, wb->prev_l, wb->prev_n * sizeof(wb->prev_l[0]));
99 memmove(wb->prev_h + 1, wb->prev_h, wb->prev_n * sizeof(wb->prev_h[0]));
100 memmove(wb->prev_c + 1, wb->prev_c, wb->prev_n * sizeof(wb->prev_c[0]));
101 wb->prev_l[0] = ll;
102 wb->prev_h[0] = wb->h;
103 strcpy(wb->prev_c[0], c);
104 wb->prev_n++;
105 wb_prevok(wb);
108 /* strip the last i characters from wb */
109 static void wb_prevpop(struct wb *wb, int i)
111 int n = wb->prev_n - i;
112 sbuf_cut(&wb->sbuf, wb->prev_l[i - 1]);
113 wb->h = wb->prev_h[i - 1];
114 memmove(wb->prev_l, wb->prev_l + i, n * sizeof(wb->prev_l[0]));
115 memmove(wb->prev_h, wb->prev_h + i, n * sizeof(wb->prev_h[0]));
116 memmove(wb->prev_c, wb->prev_c + i, n * sizeof(wb->prev_c[0]));
117 wb->prev_n = n;
118 wb->prev_ll = sbuf_len(&wb->sbuf);
121 /* return the i-th last character inserted via wb_put() */
122 static char *wb_prev(struct wb *wb, int i)
124 wb_prevcheck(wb);
125 return i < wb->prev_n ? wb->prev_c[i] : NULL;
128 void wb_put(struct wb *wb, char *c)
130 struct glyph *g;
131 int ll;
132 if (c[0] == '\n') {
133 wb->part = 0;
134 return;
136 if (c[0] == ' ') {
137 wb_hmov(wb, spacewid(R_F(wb), R_S(wb)));
138 return;
140 if (c[0] == '\t' || c[0] == '\x01' ||
141 (c[0] == c_ni && (c[1] == '\t' || c[1] == '\x01'))) {
142 sbuf_append(&wb->sbuf, c);
143 return;
145 g = dev_glyph(c, R_F(wb));
146 wb_font(wb);
147 wb_prevcheck(wb); /* make sure wb->prev_c[] is valid */
148 ll = sbuf_len(&wb->sbuf); /* sbuf length before inserting c */
149 if (!c[1] || c[0] == c_ec || c[0] == c_ni || utf8one(c)) {
150 if (c[0] == c_ni && c[1] == c_ec)
151 sbuf_printf(&wb->sbuf, "%c%c", c_ec, c_ec);
152 else
153 sbuf_append(&wb->sbuf, c);
154 } else {
155 if (c[1] && !c[2])
156 sbuf_printf(&wb->sbuf, "%c(%s", c_ec, c);
157 else
158 sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
160 if (strcmp(c_hc, c) && strcmp(c_bp, c)) {
161 wb_prevput(wb, c, ll);
162 wb->h += charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
163 wb->ct |= g ? g->type : 0;
164 wb_stsb(wb);
168 /* just like wb_put(), but call cdef_expand() if c is defined */
169 void wb_putexpand(struct wb *wb, char *c)
171 if (cdef_expand(wb, c, R_F(wb)))
172 wb_put(wb, c);
175 /* return zero if c formed a ligature with its previous character */
176 int wb_lig(struct wb *wb, char *c)
178 char lig[GNLEN] = "";
179 char *cs[LIGLEN + 2];
180 int i = -1;
181 int ligpos;
182 wb_font(wb); /* apply font changes */
183 cs[0] = c;
184 while (wb_prev(wb, ++i))
185 cs[i + 1] = wb_prev(wb, i);
186 ligpos = font_lig(dev_font(R_F(wb)), cs, i + 1);
187 if (ligpos > 1) {
188 for (i = 0; i < ligpos - 1; i++)
189 strcat(lig, wb_prev(wb, ligpos - i - 2));
190 strcat(lig, c);
191 wb_prevpop(wb, ligpos - 1);
192 wb_put(wb, lig);
193 return 0;
195 return 1;
198 /* return 0 if pairwise kerning was done */
199 int wb_kern(struct wb *wb, char *c)
201 int val;
202 wb_font(wb); /* apply font changes */
203 if (!wb_prev(wb, 0))
204 return 1;
205 val = font_kern(dev_font(R_F(wb)), wb_prev(wb, 0), c);
206 if (val)
207 wb_hmov(wb, charwid(R_F(wb), R_S(wb), val));
208 wb_prevok(wb); /* kerning should not prevent ligatures */
209 return !val;
212 int wb_part(struct wb *wb)
214 return wb->part;
217 void wb_setpart(struct wb *wb)
219 wb->part = 1;
222 void wb_drawl(struct wb *wb, int c, int h, int v)
224 wb_font(wb);
225 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
226 wb->h += h;
227 wb->v += v;
228 wb_stsb(wb);
231 void wb_drawc(struct wb *wb, int c, int r)
233 wb_font(wb);
234 sbuf_printf(&wb->sbuf, "%cD'%c %du'", c_ec, c, r);
235 wb->h += r;
238 void wb_drawe(struct wb *wb, int c, int h, int v)
240 wb_font(wb);
241 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
242 wb->h += h;
245 void wb_drawa(struct wb *wb, int c, int h1, int v1, int h2, int v2)
247 wb_font(wb);
248 sbuf_printf(&wb->sbuf, "%cD'%c %du %du %du %du'", c_ec, c, h1, v1, h2, v2);
249 wb->h += h1 + h2;
250 wb->v += v1 + v2;
251 wb_stsb(wb);
254 void wb_drawxbeg(struct wb *wb, int c)
256 wb_font(wb);
257 sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
260 void wb_drawxdot(struct wb *wb, int h, int v)
262 sbuf_printf(&wb->sbuf, " %du %du", h, v);
263 wb->h += h;
264 wb->v += v;
265 wb_stsb(wb);
268 void wb_drawxend(struct wb *wb)
270 sbuf_printf(&wb->sbuf, "'");
273 static void wb_reset(struct wb *wb)
275 wb_done(wb);
276 wb_init(wb);
279 static void wb_putc(struct wb *wb, int t, char *s)
281 switch (t) {
282 case 0:
283 case 'C':
284 wb_put(wb, s);
285 break;
286 case 'D':
287 ren_dcmd(wb, s);
288 break;
289 case 'f':
290 wb->r_f = atoi(s);
291 break;
292 case 'h':
293 wb_hmov(wb, atoi(s));
294 break;
295 case 'm':
296 wb->r_m = clr_get(s);
297 break;
298 case 's':
299 wb->r_s = atoi(s);
300 break;
301 case 'v':
302 wb_vmov(wb, atoi(s));
303 break;
304 case 'x':
305 wb_els(wb, atoi(s));
306 break;
307 case 'X':
308 wb_etc(wb, s);
309 break;
313 void wb_cat(struct wb *wb, struct wb *src)
315 char *s = sbuf_buf(&src->sbuf);
316 char d[ILNLEN];
317 int c, part;
318 while ((c = escread(&s, d)) >= 0)
319 wb_putc(wb, c, d);
320 part = src->part;
321 wb->r_s = -1;
322 wb->r_f = -1;
323 wb->r_m = -1;
324 wb_reset(src);
325 src->part = part;
328 int wb_wid(struct wb *wb)
330 return wb->h;
333 int wb_empty(struct wb *wb)
335 return sbuf_empty(&wb->sbuf);
338 /* return 1 if wb ends a sentence (.?!) */
339 int wb_eos(struct wb *wb)
341 int i = 0;
342 while (wb_prev(wb, i) && strchr("'\")]*", wb_prev(wb, i)[0]))
343 i++;
344 return wb_prev(wb, i) && strchr(".?!", wb_prev(wb, i)[0]);
347 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb)
349 *ct = wb->ct;
350 *st = -wb->st;
351 *sb = -wb->sb;
354 /* skip troff requests; return 1 if read c_hc */
355 static int skipreqs(char **s, struct wb *w1)
357 char d[ILNLEN];
358 char *r = *s;
359 int c;
360 wb_reset(w1);
361 while ((c = escread(s, d)) > 0) {
362 wb_putc(w1, c, d);
363 r = *s;
365 if (c < 0 || !strcmp(c_hc, d))
366 return 1;
367 *s = r;
368 return 0;
371 /* the position marked with hyphens or \: */
372 static char *bp_pos(char *s, int w, struct wb *w1, int flg)
374 char d[ILNLEN];
375 char *r = NULL;
376 int c;
377 skipreqs(&s, w1);
378 while ((c = escread(&s, d)) >= 0) {
379 wb_putc(w1, c, d);
380 if (wb_wid(w1) > w && (!(flg & HY_ANY) || r))
381 continue;
382 if (!c && (!strcmp("-", d) || (!strcmp("em", d) ||
383 !strcmp("hy", d)) || !strcmp(c_bp, d)))
384 r = s;
386 return r;
389 static int wb_dashwid(struct wb *wb)
391 struct glyph *g = dev_glyph("hy", R_F(wb));
392 return charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
395 /* the position marked with \% */
396 static char *hc_pos(char *s, int w, struct wb *w1, int flg)
398 char d[ILNLEN];
399 char *r = NULL;
400 int c;
401 skipreqs(&s, w1);
402 while ((c = escread(&s, d)) >= 0) {
403 wb_putc(w1, c, d);
404 if (wb_wid(w1) + wb_dashwid(w1) > w && (!(flg & HY_ANY) || r))
405 continue;
406 if (!c && !strcmp(c_hc, d))
407 r = s;
409 return r;
412 static char *hyphpos(char *s, int w, struct wb *w1, int flg)
414 char word[ILNLEN]; /* word to pass to hyphenate() */
415 char hyph[ILNLEN]; /* hyphenation points returned from hyphenate() */
416 char *iw[ILNLEN]; /* beginning of i-th char in word */
417 char *is[ILNLEN]; /* beginning of i-th char in s */
418 int fits[ILNLEN]; /* fits[i] is 1, if the first i chars fit w */
419 int n = 0; /* the number of characters in word */
420 char d[ILNLEN];
421 char *prev_s = s;
422 char *r = NULL;
423 char *wp = word, *we = word + sizeof(word);
424 int i, c;
425 skipreqs(&s, w1);
426 while ((c = escread(&s, d)) >= 0 && (c > 0 || strlen(d) + 1 < we - wp)) {
427 fits[n] = wb_wid(w1) + wb_dashwid(w1) <= w;
428 wb_putc(w1, c, d);
429 if (c == 0) {
430 iw[n] = wp;
431 is[n] = prev_s;
432 /* ignore multi-char aliases except for ligatures */
433 if (!utf8one(d) && !font_islig(dev_font(R_F(w1)), d))
434 strcpy(d, ".");
435 strcpy(wp, d);
436 wp = strchr(wp, '\0');
437 n++;
439 prev_s = s;
441 if (n < 3)
442 return NULL;
443 hyphenate(hyph, word, flg);
444 for (i = 1; i < n - 1; i++)
445 if (hyph[iw[i] - word] && (fits[i] || ((flg & HY_ANY) && !r)))
446 r = is[i];
447 return r;
450 static void dohyph(char *s, char *pos, int dash, struct wb *w1, struct wb *w2)
452 char d[ILNLEN];
453 int c = -1;
454 wb_reset(w1);
455 wb_reset(w2);
456 while (s != pos && (c = escread(&s, d)) >= 0)
457 wb_putc(w1, c, d);
458 if (dash)
459 wb_putc(w1, 0, "hy");
460 w2->r_s = w1->r_s;
461 w2->r_f = w1->r_f;
462 w2->r_m = w1->r_m;
463 while ((c = escread(&s, d)) >= 0)
464 wb_putc(w2, c, d);
467 /* hyphenate wb into w1 and w2; return zero on success */
468 int wb_hyph(struct wb *wb, int w, struct wb *w1, struct wb *w2, int flg)
470 char *s = sbuf_buf(&wb->sbuf);
471 char *dp, *hp, *p;
472 if (skipreqs(&s, w1))
473 return 1;
474 dp = bp_pos(sbuf_buf(&wb->sbuf), w, w1, flg);
475 hp = hc_pos(sbuf_buf(&wb->sbuf), w, w1, flg);
476 if (hp && dp)
477 p = flg & HY_ANY ? MIN(dp, hp) : MAX(dp, hp);
478 else
479 p = dp ? dp : hp;
480 if (!p && flg & HY_MASK)
481 p = hyphpos(sbuf_buf(&wb->sbuf), w, w1, flg);
482 if (p)
483 dohyph(sbuf_buf(&wb->sbuf), p, p != dp, w1, w2);
484 return !p;