ren: do not use adj struct to implement .tl
[neatroff.git] / wb.c
blobbb1cd985d76d04639725b50397588cbd625eef1e
1 /* word buffer */
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "roff.h"
7 /* the current font, size and color */
8 #define R_F(wb) ((wb)->r_f >= 0 ? (wb)->r_f : n_f) /* current font */
9 #define R_S(wb) ((wb)->r_s >= 0 ? (wb)->r_s : n_s) /* current size */
10 #define R_M(wb) ((wb)->r_m >= 0 ? (wb)->r_m : n_m) /* current color */
11 /* italic correction */
12 #define glyph_ic(g) (MAX(0, (g)->urx - (g)->wid))
13 #define glyph_icleft(g) (MAX(0, -(g)->llx))
14 /* like DEVWID() but handles negative w */
15 #define SDEVWID(sz, w) ((w) >= 0 ? DEVWID((sz), (w)) : -DEVWID((sz), -(w)))
16 /* the maximum and minimum values of bounding box coordinates */
17 #define BBMAX (1 << 29)
18 #define BBMIN -BBMAX
20 void wb_init(struct wb *wb)
22 memset(wb, 0, sizeof(*wb));
23 sbuf_init(&wb->sbuf);
24 wb->f = -1;
25 wb->s = -1;
26 wb->m = -1;
27 wb->r_f = -1;
28 wb->r_s = -1;
29 wb->r_m = -1;
30 wb->icleft_ll = -1;
31 wb->llx = BBMAX;
32 wb->lly = BBMAX;
33 wb->urx = BBMIN;
34 wb->ury = BBMIN;
37 void wb_done(struct wb *wb)
39 sbuf_done(&wb->sbuf);
42 /* update wb->st and wb->sb */
43 static void wb_stsb(struct wb *wb)
45 wb->st = MIN(wb->st, wb->v - (wb->s * SC_IN / 72));
46 wb->sb = MAX(wb->sb, wb->v);
49 /* update bounding box */
50 static void wb_bbox(struct wb *wb, int llx, int lly, int urx, int ury)
52 wb->llx = MIN(wb->llx, wb->h + llx);
53 wb->lly = MIN(wb->lly, -wb->v + lly);
54 wb->urx = MAX(wb->urx, wb->h + urx);
55 wb->ury = MAX(wb->ury, -wb->v + ury);
58 /* append font and size to the buffer if needed */
59 static void wb_font(struct wb *wb)
61 if (wb->f != R_F(wb)) {
62 sbuf_printf(&wb->sbuf, "%cf(%02d", c_ec, R_F(wb));
63 wb->f = R_F(wb);
65 if (wb->s != R_S(wb)) {
66 sbuf_printf(&wb->sbuf, "%cs(%02d", c_ec, R_S(wb));
67 wb->s = R_S(wb);
69 if (!n_cp && wb->m != R_M(wb)) {
70 sbuf_printf(&wb->sbuf, "%cm[%s]", c_ec, clr_str(R_M(wb)));
71 wb->m = R_M(wb);
73 wb_stsb(wb);
76 /* pending font, size or color changes */
77 static int wb_pendingfont(struct wb *wb)
79 return wb->f != R_F(wb) || wb->s != R_S(wb) ||
80 (!n_cp && wb->m != R_M(wb));
83 void wb_hmov(struct wb *wb, int n)
85 wb->h += n;
86 sbuf_printf(&wb->sbuf, "%ch'%du'", c_ec, n);
89 void wb_vmov(struct wb *wb, int n)
91 wb->v += n;
92 sbuf_printf(&wb->sbuf, "%cv'%du'", c_ec, n);
95 void wb_els(struct wb *wb, int els)
97 if (els > wb->els_pos)
98 wb->els_pos = els;
99 if (els < wb->els_neg)
100 wb->els_neg = els;
101 sbuf_printf(&wb->sbuf, "%cx'%du'", c_ec, els);
104 void wb_etc(struct wb *wb, char *x)
106 wb_font(wb);
107 sbuf_printf(&wb->sbuf, "%cX\x02%s\x02", c_ec, x);
110 /* make sure nothing is appended to wb after the last wb_put() */
111 static void wb_prevcheck(struct wb *wb)
113 if (wb->prev_ll != sbuf_len(&wb->sbuf))
114 wb->prev_n = 0;
117 /* mark wb->prev_c[] as valid */
118 static void wb_prevok(struct wb *wb)
120 wb->prev_ll = sbuf_len(&wb->sbuf);
123 /* append c to wb->prev_c[] */
124 static void wb_prevput(struct wb *wb, char *c, int ll)
126 if (wb->prev_n == LEN(wb->prev_c))
127 wb->prev_n--;
128 memmove(wb->prev_l + 1, wb->prev_l, wb->prev_n * sizeof(wb->prev_l[0]));
129 memmove(wb->prev_h + 1, wb->prev_h, wb->prev_n * sizeof(wb->prev_h[0]));
130 memmove(wb->prev_c + 1, wb->prev_c, wb->prev_n * sizeof(wb->prev_c[0]));
131 wb->prev_l[0] = ll;
132 wb->prev_h[0] = wb->h;
133 strcpy(wb->prev_c[0], c);
134 wb->prev_n++;
135 wb_prevok(wb);
138 /* strip the last i characters from wb */
139 static void wb_prevpop(struct wb *wb, int i)
141 int n = wb->prev_n - i;
142 sbuf_cut(&wb->sbuf, wb->prev_l[i - 1]);
143 wb->h = wb->prev_h[i - 1];
144 memmove(wb->prev_l, wb->prev_l + i, n * sizeof(wb->prev_l[0]));
145 memmove(wb->prev_h, wb->prev_h + i, n * sizeof(wb->prev_h[0]));
146 memmove(wb->prev_c, wb->prev_c + i, n * sizeof(wb->prev_c[0]));
147 wb->prev_n = n;
148 wb->prev_ll = sbuf_len(&wb->sbuf);
151 /* return the i-th last character inserted via wb_put() */
152 static char *wb_prev(struct wb *wb, int i)
154 wb_prevcheck(wb);
155 return i < wb->prev_n ? wb->prev_c[i] : NULL;
158 static struct glyph *wb_prevglyph(struct wb *wb)
160 return wb_prev(wb, 0) ? dev_glyph(wb_prev(wb, 0), wb->f) : NULL;
163 void wb_put(struct wb *wb, char *c)
165 struct glyph *g;
166 int ll, zerowidth;
167 if (c[0] == '\n') {
168 wb->part = 0;
169 return;
171 if (c[0] == ' ') {
172 wb_hmov(wb, spacewid(R_F(wb), R_S(wb)));
173 return;
175 if (c[0] == '\t' || c[0] == '\x01' ||
176 (c[0] == c_ni && (c[1] == '\t' || c[1] == '\x01'))) {
177 sbuf_append(&wb->sbuf, c);
178 return;
180 g = dev_glyph(c, R_F(wb));
181 zerowidth = !strcmp(c_hc, c) || !strcmp(c_bp, c);
182 if (!g && c[0] == c_ec && !zerowidth) { /* unknown escape */
183 memmove(c, c + 1, strlen(c));
184 g = dev_glyph(c, R_F(wb));
186 if (g && !zerowidth && wb->icleft_ll == sbuf_len(&wb->sbuf))
187 if (glyph_icleft(g))
188 wb_hmov(wb, SDEVWID(R_S(wb), glyph_icleft(g)));
189 wb->icleft_ll = -1;
190 wb_font(wb);
191 wb_prevcheck(wb); /* make sure wb->prev_c[] is valid */
192 ll = sbuf_len(&wb->sbuf); /* sbuf length before inserting c */
193 if (!c[1] || c[0] == c_ec || c[0] == c_ni || utf8one(c)) {
194 if (c[0] == c_ni && c[1] == c_ec)
195 sbuf_printf(&wb->sbuf, "%c%c", c_ec, c_ec);
196 else
197 sbuf_append(&wb->sbuf, c);
198 } else {
199 if (c[1] && !c[2])
200 sbuf_printf(&wb->sbuf, "%c(%s", c_ec, c);
201 else
202 sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
204 if (!zerowidth) {
205 wb_prevput(wb, c, ll);
206 if (!n_cp && g)
207 wb_bbox(wb, SDEVWID(wb->s, g->llx),
208 SDEVWID(wb->s, g->lly),
209 SDEVWID(wb->s, g->urx),
210 SDEVWID(wb->s, g->ury));
211 wb->h += charwid(wb->f, wb->s, g ? g->wid : 0);
212 wb->ct |= g ? g->type : 0;
213 wb_stsb(wb);
217 /* just like wb_put(), but call cdef_expand() if c is defined */
218 void wb_putexpand(struct wb *wb, char *c)
220 if (cdef_expand(wb, c, R_F(wb)))
221 wb_put(wb, c);
224 /* return zero if c formed a ligature with its previous character */
225 int wb_lig(struct wb *wb, char *c)
227 char lig[GNLEN] = "";
228 char *cs[LIGLEN + 2];
229 int i = -1;
230 int ligpos;
231 if (wb_pendingfont(wb)) /* font changes disable ligatures */
232 return 1;
233 cs[0] = c;
234 while (wb_prev(wb, ++i))
235 cs[i + 1] = wb_prev(wb, i);
236 ligpos = font_lig(dev_font(R_F(wb)), cs, i + 1);
237 if (ligpos > 1) {
238 for (i = 0; i < ligpos - 1; i++)
239 strcat(lig, wb_prev(wb, ligpos - i - 2));
240 strcat(lig, c);
241 wb_prevpop(wb, ligpos - 1);
242 wb_put(wb, lig);
243 return 0;
245 return 1;
248 /* return 0 if pairwise kerning was done */
249 int wb_kern(struct wb *wb, char *c)
251 int val;
252 if (wb_pendingfont(wb) || !wb_prev(wb, 0))
253 return 1;
254 val = font_kern(dev_font(R_F(wb)), wb_prev(wb, 0), c);
255 if (val)
256 wb_hmov(wb, charwid(R_F(wb), R_S(wb), val));
257 wb_prevok(wb); /* kerning should not prevent ligatures */
258 return !val;
261 int wb_part(struct wb *wb)
263 return wb->part;
266 void wb_setpart(struct wb *wb)
268 wb->part = 1;
271 void wb_drawl(struct wb *wb, int c, int h, int v)
273 wb_font(wb);
274 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
275 wb->h += h;
276 wb->v += v;
277 wb_stsb(wb);
280 void wb_drawc(struct wb *wb, int c, int r)
282 wb_font(wb);
283 sbuf_printf(&wb->sbuf, "%cD'%c %du'", c_ec, c, r);
284 wb->h += r;
287 void wb_drawe(struct wb *wb, int c, int h, int v)
289 wb_font(wb);
290 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
291 wb->h += h;
294 void wb_drawa(struct wb *wb, int c, int h1, int v1, int h2, int v2)
296 wb_font(wb);
297 sbuf_printf(&wb->sbuf, "%cD'%c %du %du %du %du'", c_ec, c, h1, v1, h2, v2);
298 wb->h += h1 + h2;
299 wb->v += v1 + v2;
300 wb_stsb(wb);
303 void wb_drawxbeg(struct wb *wb, int c)
305 wb_font(wb);
306 sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
309 void wb_drawxdot(struct wb *wb, int h, int v)
311 sbuf_printf(&wb->sbuf, " %du %du", h, v);
312 wb->h += h;
313 wb->v += v;
314 wb_stsb(wb);
317 void wb_drawxend(struct wb *wb)
319 sbuf_printf(&wb->sbuf, "'");
322 static void wb_reset(struct wb *wb)
324 wb_done(wb);
325 wb_init(wb);
328 static void wb_putc(struct wb *wb, int t, char *s)
330 switch (t) {
331 case 0:
332 case 'C':
333 wb_put(wb, s);
334 break;
335 case 'D':
336 ren_dcmd(wb, s);
337 break;
338 case 'f':
339 wb->r_f = atoi(s);
340 break;
341 case 'h':
342 wb_hmov(wb, atoi(s));
343 break;
344 case 'm':
345 wb->r_m = clr_get(s);
346 break;
347 case 's':
348 wb->r_s = atoi(s);
349 break;
350 case 'v':
351 wb_vmov(wb, atoi(s));
352 break;
353 case 'x':
354 wb_els(wb, atoi(s));
355 break;
356 case 'X':
357 wb_etc(wb, s);
358 break;
362 void wb_cat(struct wb *wb, struct wb *src)
364 char *s = sbuf_buf(&src->sbuf);
365 char d[ILNLEN];
366 int c, part;
367 while ((c = escread(&s, d)) >= 0)
368 wb_putc(wb, c, d);
369 part = src->part;
370 wb->r_s = -1;
371 wb->r_f = -1;
372 wb->r_m = -1;
373 wb_reset(src);
374 src->part = part;
377 int wb_wid(struct wb *wb)
379 return wb->h;
382 int wb_empty(struct wb *wb)
384 return sbuf_empty(&wb->sbuf);
387 /* return 1 if wb ends a sentence (.?!) */
388 int wb_eos(struct wb *wb)
390 int i = 0;
391 while (wb_prev(wb, i) && strchr("'\")]*", wb_prev(wb, i)[0]))
392 i++;
393 return wb_prev(wb, i) && strchr(".?!", wb_prev(wb, i)[0]);
396 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb,
397 int *llx, int *lly, int *urx, int *ury)
399 *ct = wb->ct;
400 *st = -wb->st;
401 *sb = -wb->sb;
402 *llx = wb->llx < BBMAX ? wb->llx : 0;
403 *lly = wb->lly < BBMAX ? -wb->lly : 0;
404 *urx = wb->urx > BBMIN ? wb->urx : 0;
405 *ury = wb->ury > BBMIN ? -wb->ury : 0;
408 /* skip troff requests; return 1 if read c_hc */
409 static int skipreqs(char **s, struct wb *w1)
411 char d[ILNLEN];
412 char *r = *s;
413 int c;
414 wb_reset(w1);
415 while ((c = escread(s, d)) > 0) {
416 wb_putc(w1, c, d);
417 r = *s;
419 if (c < 0 || !strcmp(c_hc, d))
420 return 1;
421 *s = r;
422 return 0;
425 /* the position marked with hyphens or \: */
426 static char *bp_pos(char *s, int w, struct wb *w1, int flg)
428 char d[ILNLEN];
429 char *r = NULL;
430 int c;
431 skipreqs(&s, w1);
432 while ((c = escread(&s, d)) >= 0) {
433 wb_putc(w1, c, d);
434 if (wb_wid(w1) > w && (!(flg & HY_ANY) || r))
435 continue;
436 if (!c && (!strcmp("-", d) || (!strcmp("em", d) ||
437 !strcmp("hy", d)) || !strcmp(c_bp, d)))
438 r = s;
440 return r;
443 static int wb_dashwid(struct wb *wb)
445 struct glyph *g = dev_glyph("hy", R_F(wb));
446 return charwid(R_F(wb), R_S(wb), g ? g->wid : 0);
449 /* the position marked with \% */
450 static char *hc_pos(char *s, int w, struct wb *w1, int flg)
452 char d[ILNLEN];
453 char *r = NULL;
454 int c;
455 skipreqs(&s, w1);
456 while ((c = escread(&s, d)) >= 0) {
457 wb_putc(w1, c, d);
458 if (wb_wid(w1) + wb_dashwid(w1) > w && (!(flg & HY_ANY) || r))
459 continue;
460 if (!c && !strcmp(c_hc, d))
461 r = s;
463 return r;
466 static char *hyphpos(char *s, int w, struct wb *w1, int flg)
468 char word[ILNLEN]; /* word to pass to hyphenate() */
469 char hyph[ILNLEN]; /* hyphenation points returned from hyphenate() */
470 char *iw[ILNLEN]; /* beginning of i-th char in word */
471 char *is[ILNLEN]; /* beginning of i-th char in s */
472 int fits[ILNLEN]; /* fits[i] is 1, if the first i chars fit w */
473 int n = 0; /* the number of characters in word */
474 char d[ILNLEN];
475 char *prev_s = s;
476 char *r = NULL;
477 char *wp = word, *we = word + sizeof(word);
478 int i, c;
479 skipreqs(&s, w1);
480 while ((c = escread(&s, d)) >= 0 && (c > 0 || strlen(d) + 1 < we - wp)) {
481 fits[n] = wb_wid(w1) + wb_dashwid(w1) <= w;
482 wb_putc(w1, c, d);
483 if (c == 0) {
484 iw[n] = wp;
485 is[n] = prev_s;
486 /* ignore multi-char aliases except for ligatures */
487 if (!utf8one(d) && !font_islig(dev_font(R_F(w1)), d))
488 strcpy(d, ".");
489 strcpy(wp, d);
490 wp = strchr(wp, '\0');
491 n++;
493 prev_s = s;
495 if (n < 3)
496 return NULL;
497 hyphenate(hyph, word, flg);
498 for (i = 1; i < n - 1; i++)
499 if (hyph[iw[i] - word] && (fits[i] || ((flg & HY_ANY) && !r)))
500 r = is[i];
501 return r;
504 static void dohyph(char *s, char *pos, int dash, struct wb *w1, struct wb *w2)
506 char d[ILNLEN];
507 int c = -1;
508 wb_reset(w1);
509 wb_reset(w2);
510 while (s != pos && (c = escread(&s, d)) >= 0)
511 wb_putc(w1, c, d);
512 if (dash)
513 wb_putc(w1, 0, "hy");
514 w2->r_s = w1->r_s;
515 w2->r_f = w1->r_f;
516 w2->r_m = w1->r_m;
517 while ((c = escread(&s, d)) >= 0)
518 wb_putc(w2, c, d);
521 /* hyphenate wb into w1 and w2; return zero on success */
522 int wb_hyph(struct wb *wb, int w, struct wb *w1, struct wb *w2, int flg)
524 char *s = sbuf_buf(&wb->sbuf);
525 char *dp, *hp, *p;
526 if (skipreqs(&s, w1))
527 return 1;
528 dp = bp_pos(sbuf_buf(&wb->sbuf), w, w1, flg);
529 hp = hc_pos(sbuf_buf(&wb->sbuf), w, w1, flg);
530 if (hp && dp)
531 p = flg & HY_ANY ? MIN(dp, hp) : MAX(dp, hp);
532 else
533 p = dp ? dp : hp;
534 if (!p && flg & HY_MASK)
535 p = hyphpos(sbuf_buf(&wb->sbuf), w, w1, flg);
536 if (p)
537 dohyph(sbuf_buf(&wb->sbuf), p, p != dp, w1, w2);
538 return !p;
541 void wb_italiccorrection(struct wb *wb)
543 struct glyph *g = wb_prevglyph(wb);
544 if (g && glyph_ic(g))
545 wb_hmov(wb, SDEVWID(wb->s, glyph_ic(g)));
548 void wb_italiccorrectionleft(struct wb *wb)
550 wb->icleft_ll = sbuf_len(&wb->sbuf);