tr: resetting font script and language
[neatroff.git] / wb.c
blobf141633aa3d70a6f847fad1a8bcf4b13009d919a
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);
572 static char *keshideh_chars[] = {
573 "ﺒ", "ﺑ", "ﭙ", "ﭘ", "ﺘ", "ﺗ", "ﺜ", "ﺛ", "ﺴ", "ﺳ",
574 "ﺸ", "ﺷ", "ﻔ", "ﻓ", "ﻘ", "ﻗ", "ﮑ", "ﮐ", "ﮕ", "ﮔ",
575 "ﻤ", "ﻣ", "ﻨ", "ﻧ", "ﻬ", "ﻫ", "ﯿ", "ﯾ",
578 static int keshideh(char *c)
580 int i;
581 for (i = 0; i < LEN(keshideh_chars); i++)
582 if (!strcmp(keshideh_chars[i], c))
583 return 1;
584 return 0;
587 /* insert keshideh */
588 int wb_keshideh(char *word, struct wb *dst, int wid)
590 char p[GNLEN] = "";
591 char *s, *d, *s_prev = NULL, *s_kesh = NULL;
592 int ins = 0;
593 int c;
594 /* find the last keshideh position */
595 s = word;
596 while ((c = escread(&s, &d)) >= 0) {
597 wb_putc(dst, c, d);
598 if (!c && keshideh(p)) {
599 struct glyph *g = dev_glyph("ـ", R_F(dst));
600 int kw = g ? font_gwid(g->font,
601 dev_font(R_F(dst)), R_S(dst), g->wid) : 0;
602 if (g && kw < wid) {
603 s_kesh = s_prev;
604 ins = kw;
607 s_prev = s;
608 strcpy(p, c ? "" : d);
610 /* insert the keshideh at s_kesh */
611 s = word;
612 wb_reset(dst);
613 while ((c = escread(&s, &d)) >= 0) {
614 wb_putc(dst, c, d);
615 if (s == s_kesh)
616 wb_putc(dst, 0, "ـ");
618 return ins;