dir: support text direction with .>>, .<<, \> and \<
[neatroff.git] / wb.c
blob8203914ed2998abed19105f01e1d9dd83cefa33c
1 /* word buffer */
2 #include <ctype.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "roff.h"
8 /* the current font, size and color */
9 #define R_F(wb) ((wb)->r_f >= 0 ? (wb)->r_f : n_f) /* current font */
10 #define R_S(wb) ((wb)->r_s >= 0 ? (wb)->r_s : n_s) /* current size */
11 #define R_M(wb) ((wb)->r_m >= 0 ? (wb)->r_m : n_m) /* current color */
12 #define R_CD(b) ((wb)->r_cd >= 0 ? (wb)->r_cd : n_cd) /* current direction */
13 /* italic correction */
14 #define glyph_ic(g) (MAX(0, (g)->urx - (g)->wid))
15 #define glyph_icleft(g) (MAX(0, -(g)->llx))
16 /* the maximum and minimum values of bounding box coordinates */
17 #define BBMAX (1 << 29)
18 #define BBMIN -BBMAX
20 static void wb_flushsub(struct wb *wb);
22 void wb_init(struct wb *wb)
24 memset(wb, 0, sizeof(*wb));
25 sbuf_init(&wb->sbuf);
26 wb->sub_collect = 1;
27 wb->f = -1;
28 wb->s = -1;
29 wb->m = -1;
30 wb->cd = -1;
31 wb->r_f = -1;
32 wb->r_s = -1;
33 wb->r_m = -1;
34 wb->r_cd = -1;
35 wb->llx = BBMAX;
36 wb->lly = BBMAX;
37 wb->urx = BBMIN;
38 wb->ury = BBMIN;
41 void wb_done(struct wb *wb)
43 sbuf_done(&wb->sbuf);
46 /* update wb->st and wb->sb */
47 static void wb_stsb(struct wb *wb)
49 wb->st = MIN(wb->st, wb->v - (wb->s * SC_IN / 72));
50 wb->sb = MAX(wb->sb, wb->v);
53 /* update bounding box */
54 static void wb_bbox(struct wb *wb, int llx, int lly, int urx, int ury)
56 wb->llx = MIN(wb->llx, wb->h + llx);
57 wb->lly = MIN(wb->lly, -wb->v + lly);
58 wb->urx = MAX(wb->urx, wb->h + urx);
59 wb->ury = MAX(wb->ury, -wb->v + ury);
62 /* pending font, size or color changes */
63 static int wb_pendingfont(struct wb *wb)
65 return wb->f != R_F(wb) || wb->s != R_S(wb) ||
66 (!n_cp && wb->m != R_M(wb));
69 /* pending direction change */
70 static int wb_pendingdir(struct wb *wb)
72 return wb->cd != R_CD(wb);
75 /* append font and size to the buffer if needed */
76 static void wb_flushfont(struct wb *wb)
78 if (wb->f != R_F(wb)) {
79 sbuf_printf(&wb->sbuf, "%cf(%02d", c_ec, R_F(wb));
80 wb->f = R_F(wb);
82 if (wb->s != R_S(wb)) {
83 if (R_S(wb) < 100)
84 sbuf_printf(&wb->sbuf, "%cs(%02d", c_ec, R_S(wb));
85 else
86 sbuf_printf(&wb->sbuf, "%cs[%d]", c_ec, R_S(wb));
87 wb->s = R_S(wb);
89 if (!n_cp && wb->m != R_M(wb)) {
90 sbuf_printf(&wb->sbuf, "%cm[%s]", c_ec, clr_str(R_M(wb)));
91 wb->m = R_M(wb);
93 wb_stsb(wb);
96 /* append current text direction to the buffer if needed */
97 void wb_flushdir(struct wb *wb)
99 if (wb->cd != R_CD(wb)) {
100 wb_flushsub(wb);
101 if (dir_do)
102 sbuf_printf(&wb->sbuf, "%c%c", c_ec, R_CD(wb) > 0 ? '<' : '>');
103 wb->cd = R_CD(wb);
107 /* apply font and size changes and flush the collected subword */
108 static void wb_flush(struct wb *wb)
110 wb_flushsub(wb);
111 wb_flushdir(wb);
112 wb_flushfont(wb);
115 void wb_hmov(struct wb *wb, int n)
117 wb_flushsub(wb);
118 wb->h += n;
119 sbuf_printf(&wb->sbuf, "%ch'%du'", c_ec, n);
122 void wb_vmov(struct wb *wb, int n)
124 wb_flushsub(wb);
125 wb->v += n;
126 sbuf_printf(&wb->sbuf, "%cv'%du'", c_ec, n);
129 void wb_els(struct wb *wb, int els)
131 wb_flushsub(wb);
132 if (els > wb->els_pos)
133 wb->els_pos = els;
134 if (els < wb->els_neg)
135 wb->els_neg = els;
136 sbuf_printf(&wb->sbuf, "%cx'%du'", c_ec, els);
139 void wb_etc(struct wb *wb, char *x)
141 wb_flush(wb);
142 sbuf_printf(&wb->sbuf, "%cX\x02%s\x02", c_ec, x);
145 static void wb_putbuf(struct wb *wb, char *c)
147 struct glyph *g;
148 int zerowidth;
149 if (c[0] == '\t' || c[0] == '\x01' ||
150 (c[0] == c_ni && (c[1] == '\t' || c[1] == '\x01'))) {
151 sbuf_append(&wb->sbuf, c);
152 return;
154 g = dev_glyph(c, wb->f);
155 zerowidth = c_hymark(c);
156 if (!g && c[0] == c_ec && !zerowidth) { /* unknown escape */
157 memmove(c, c + 1, strlen(c));
158 g = dev_glyph(c, wb->f);
160 if (g && !zerowidth && wb->icleft && glyph_icleft(g))
161 wb_hmov(wb, font_wid(g->font, wb->s, glyph_icleft(g)));
162 wb->icleft = 0;
163 if (!c[1] || c[0] == c_ec || c[0] == c_ni || utf8one(c)) {
164 if (c[0] == c_ni && c[1] == c_ec)
165 sbuf_printf(&wb->sbuf, "%c%c", c_ec, c_ec);
166 else
167 sbuf_append(&wb->sbuf, c);
168 } else {
169 if (c[1] && !c[2])
170 sbuf_printf(&wb->sbuf, "%c(%s", c_ec, c);
171 else
172 sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
174 if (!zerowidth) {
175 if (!n_cp && g)
176 wb_bbox(wb, font_wid(g->font, wb->s, g->llx),
177 font_wid(g->font, wb->s, g->lly),
178 font_wid(g->font, wb->s, g->urx),
179 font_wid(g->font, wb->s, g->ury));
180 wb->h += g ? font_gwid(g->font, dev_font(wb->f), wb->s, g->wid) : 0;
181 wb->ct |= g ? g->type : 0;
182 wb_stsb(wb);
186 /* return nonzero if it cannot be hyphenated */
187 static int wb_hyph(char src[][GNLEN], int src_n, char *src_hyph, int flg)
189 char word[WORDLEN * GNLEN]; /* word to pass to hyphenate() */
190 char hyph[WORDLEN * GNLEN]; /* hyphenation points of word */
191 int smap[WORDLEN]; /* the mapping from src[] to word[] */
192 char *s, *d;
193 int i;
194 d = word;
195 *d = '\0';
196 for (i = 0; i < src_n; i++) {
197 s = src[i];
198 smap[i] = d - word;
199 if (c_hystop(s))
200 return 1;
201 if (c_hymark(s))
202 continue;
203 d += hy_cput(d, s);
205 memset(hyph, 0, (d - word) * sizeof(hyph[0]));
206 hyphenate(hyph, word, flg);
207 for (i = 0; i < src_n; i++)
208 src_hyph[i] = hyph[smap[i]];
209 return 0;
212 static int wb_collect(struct wb *wb, int val)
214 int old = wb->sub_collect;
215 wb->sub_collect = val;
216 return old;
219 /* output the collected characters; only for those present in wb->f font */
220 static void wb_flushsub(struct wb *wb)
222 struct font *fn;
223 struct glyph *gsrc[WORDLEN];
224 struct glyph *gdst[WORDLEN];
225 int x[WORDLEN], y[WORDLEN], xadv[WORDLEN], yadv[WORDLEN];
226 int dmap[WORDLEN];
227 char src_hyph[WORDLEN];
228 int dst_n, i;
229 int sidx = 0;
230 if (!wb->sub_n || !wb->sub_collect)
231 return;
232 wb->sub_collect = 0;
233 fn = dev_font(wb->f);
234 if (!n_hy || wb_hyph(wb->sub_c, wb->sub_n, src_hyph, n_hy))
235 memset(src_hyph, 0, sizeof(src_hyph));
236 /* call font_layout() for collected glyphs; skip hyphenation marks */
237 while (sidx < wb->sub_n) {
238 int beg = sidx;
239 for (; sidx < wb->sub_n && !c_hymark(wb->sub_c[sidx]); sidx++)
240 gsrc[sidx - beg] = font_find(fn, wb->sub_c[sidx]);
241 dst_n = font_layout(fn, gsrc, sidx - beg, wb->s,
242 gdst, dmap, x, y, xadv, yadv, n_lg, n_kn);
243 for (i = 0; i < dst_n; i++) {
244 int xd[2] = {x[i], xadv[i] - x[i]};
245 int yd[2] = {y[i], yadv[i] - y[i]};
246 if (xd[wb->cd])
247 wb_hmov(wb, font_wid(fn, wb->s, xd[wb->cd]));
248 if (yd[wb->cd])
249 wb_vmov(wb, font_wid(fn, wb->s, yd[wb->cd]));
250 if (src_hyph[beg + dmap[i]])
251 wb_putbuf(wb, c_hc);
252 if (gdst[i] == gsrc[dmap[i]])
253 wb_putbuf(wb, wb->sub_c[beg + dmap[i]]);
254 else
255 wb_putbuf(wb, gdst[i]->name);
256 if (xd[1 - wb->cd])
257 wb_hmov(wb, font_wid(fn, wb->s, xd[1 - wb->cd]));
258 if (yd[1 - wb->cd])
259 wb_vmov(wb, font_wid(fn, wb->s, yd[1 - wb->cd]));
261 for (; sidx < wb->sub_n && c_hymark(wb->sub_c[sidx]); sidx++)
262 wb_putbuf(wb, wb->sub_c[sidx]);
264 wb->sub_n = 0;
265 wb->icleft = 0;
266 wb->sub_collect = 1;
269 void wb_put(struct wb *wb, char *c)
271 if (c[0] == '\n') {
272 wb->part = 0;
273 return;
275 if (wb_pendingdir(wb))
276 wb_flushdir(wb);
277 if (c[0] == ' ') {
278 wb_flushsub(wb);
279 wb_hmov(wb, font_swid(dev_font(R_F(wb)), R_S(wb), n_ss));
280 return;
282 if (!strcmp(c_nb, c)) {
283 wb_flushsub(wb);
284 sbuf_append(&wb->sbuf, c);
285 wb->h += font_swid(dev_font(R_F(wb)), R_S(wb), n_ss);
286 return;
288 if (wb_pendingfont(wb) || wb->sub_n == LEN(wb->sub_c))
289 wb_flush(wb);
290 if (wb->sub_collect) {
291 if (font_find(dev_font(wb->f), c) || c_hymark(c))
292 strcpy(wb->sub_c[wb->sub_n++], c);
293 else
294 wb_putraw(wb, c);
295 } else {
296 wb_putbuf(wb, c);
300 /* just like wb_put() but disable subword collection */
301 void wb_putraw(struct wb *wb, char *c)
303 int collect;
304 wb_flushsub(wb);
305 collect = wb_collect(wb, 0);
306 wb_put(wb, c);
307 wb_collect(wb, collect);
310 /* just like wb_put(), but call cdef_expand() if c is defined */
311 void wb_putexpand(struct wb *wb, char *c)
313 if (cdef_expand(wb, c, R_F(wb)))
314 wb_put(wb, c);
317 int wb_part(struct wb *wb)
319 return wb->part;
322 void wb_setpart(struct wb *wb)
324 wb->part = 1;
327 int wb_cost(struct wb *wb)
329 return wb->cost;
332 void wb_setcost(struct wb *wb, int cost)
334 wb->cost = cost;
337 void wb_drawl(struct wb *wb, int c, int h, int v)
339 wb_flush(wb);
340 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
341 wb->h += h;
342 wb->v += v;
343 wb_stsb(wb);
346 void wb_drawc(struct wb *wb, int c, int r)
348 wb_flush(wb);
349 sbuf_printf(&wb->sbuf, "%cD'%c %du'", c_ec, c, r);
350 wb->h += r;
353 void wb_drawe(struct wb *wb, int c, int h, int v)
355 wb_flush(wb);
356 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
357 wb->h += h;
360 void wb_drawa(struct wb *wb, int c, int h1, int v1, int h2, int v2)
362 wb_flush(wb);
363 sbuf_printf(&wb->sbuf, "%cD'%c %du %du %du %du'",
364 c_ec, c, h1, v1, h2, v2);
365 wb->h += h1 + h2;
366 wb->v += v1 + v2;
367 wb_stsb(wb);
370 void wb_drawxbeg(struct wb *wb, int c)
372 wb_flush(wb);
373 sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
376 void wb_drawxdot(struct wb *wb, int h, int v)
378 sbuf_printf(&wb->sbuf, " %du %du", h, v);
379 wb->h += h;
380 wb->v += v;
381 wb_stsb(wb);
384 void wb_drawxcmd(struct wb *wb, char *cmd)
386 sbuf_printf(&wb->sbuf, " %s", cmd);
389 void wb_drawxend(struct wb *wb)
391 sbuf_printf(&wb->sbuf, "'");
394 void wb_reset(struct wb *wb)
396 wb_done(wb);
397 wb_init(wb);
400 char *wb_buf(struct wb *wb)
402 wb_flushsub(wb);
403 return sbuf_buf(&wb->sbuf);
406 static void wb_putc(struct wb *wb, int t, char *s)
408 if (t && t != 'C')
409 wb_flushsub(wb);
410 switch (t) {
411 case 0:
412 case 'C':
413 wb_put(wb, s);
414 break;
415 case 'D':
416 ren_dcmd(wb, s);
417 break;
418 case 'f':
419 wb->r_f = atoi(s);
420 break;
421 case 'h':
422 wb_hmov(wb, atoi(s));
423 break;
424 case 'm':
425 wb->r_m = clr_get(s);
426 break;
427 case 's':
428 wb->r_s = atoi(s);
429 break;
430 case 'v':
431 wb_vmov(wb, atoi(s));
432 break;
433 case 'x':
434 wb_els(wb, atoi(s));
435 break;
436 case 'X':
437 wb_etc(wb, s);
438 break;
439 case '<':
440 case '>':
441 wb->r_cd = t == '<';
442 wb_flushdir(wb);
443 break;
447 void wb_cat(struct wb *wb, struct wb *src)
449 char *s, *d;
450 int c, part;
451 int collect;
452 wb_flushsub(src);
453 wb_flushsub(wb);
454 collect = wb_collect(wb, 0);
455 s = sbuf_buf(&src->sbuf);
456 while ((c = escread(&s, &d)) >= 0)
457 wb_putc(wb, c, d);
458 part = src->part;
459 wb->r_s = -1;
460 wb->r_f = -1;
461 wb->r_m = -1;
462 wb->r_cd = -1;
463 wb_reset(src);
464 src->part = part;
465 wb_collect(wb, collect);
468 int wb_wid(struct wb *wb)
470 wb_flushsub(wb);
471 return wb->h;
474 int wb_hpos(struct wb *wb)
476 wb_flushsub(wb);
477 return wb->h;
480 int wb_vpos(struct wb *wb)
482 wb_flushsub(wb);
483 return wb->v;
486 int wb_empty(struct wb *wb)
488 return !wb->sub_n && sbuf_empty(&wb->sbuf);
491 /* return 1 if wb ends a sentence (.?!) */
492 int wb_eos(struct wb *wb)
494 int i = wb->sub_n - 1;
495 while (i > 0 && c_eostran(wb->sub_c[i]))
496 i--;
497 return i >= 0 && c_eossent(wb->sub_c[i]);
500 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb,
501 int *llx, int *lly, int *urx, int *ury)
503 wb_flushsub(wb);
504 *ct = wb->ct;
505 *st = -wb->st;
506 *sb = -wb->sb;
507 *llx = wb->llx < BBMAX ? wb->llx : 0;
508 *lly = wb->lly < BBMAX ? -wb->lly : 0;
509 *urx = wb->urx > BBMIN ? wb->urx : 0;
510 *ury = wb->ury > BBMIN ? -wb->ury : 0;
513 static struct glyph *wb_prevglyph(struct wb *wb)
515 return wb->sub_n ? dev_glyph(wb->sub_c[wb->sub_n - 1], wb->f) : NULL;
518 void wb_italiccorrection(struct wb *wb)
520 struct glyph *g = wb_prevglyph(wb);
521 if (g && glyph_ic(g))
522 wb_hmov(wb, font_wid(g->font, wb->s, glyph_ic(g)));
525 void wb_italiccorrectionleft(struct wb *wb)
527 wb_flushsub(wb);
528 wb->icleft = 1;
531 void wb_fnszget(struct wb *wb, int *fn, int *sz, int *m, int *cd)
533 wb_flushsub(wb);
534 *fn = wb->r_f;
535 *sz = wb->r_s;
536 *m = wb->r_m;
537 *cd = wb->r_cd;
540 void wb_fnszset(struct wb *wb, int fn, int sz, int m, int cd)
542 wb->r_f = fn;
543 wb->r_s = sz;
544 wb->r_m = m;
545 wb->r_cd = cd;
548 void wb_catstr(struct wb *wb, char *s, char *end)
550 int collect, c;
551 char *d;
552 wb_flushsub(wb);
553 collect = wb_collect(wb, 0);
554 while (s < end && (c = escread(&s, &d)) >= 0)
555 wb_putc(wb, c, d);
556 wb_collect(wb, collect);
559 /* return the size of \(hy if appended to wb */
560 int wb_hywid(struct wb *wb)
562 struct glyph *g = dev_glyph("hy", wb->f);
563 return g ? font_gwid(g->font, dev_font(R_F(wb)), R_S(wb), g->wid) : 0;
566 /* return the size of space if appended to wb */
567 int wb_swid(struct wb *wb)
569 return font_swid(dev_font(R_F(wb)), R_S(wb), n_ss);