reg: use snprintf for string values in num_str()
[neatroff.git] / wb.c
bloba8b188af566a53b8ed41467f9aac46e13699d68a
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 if (g->llx || g->lly || g->urx || g->ury) {
177 int llx = font_wid(g->font, wb->s, g->llx);
178 int lly = font_wid(g->font, wb->s, g->lly);
179 int urx = font_wid(g->font, wb->s, g->urx);
180 int ury = font_wid(g->font, wb->s, g->ury);
181 wb_bbox(wb, llx, lly, urx, ury);
182 } else { /* no bounding box information */
183 int ht = wb->s * SC_PT;
184 int urx = font_wid(g->font, wb->s, g->wid);
185 int lly = (g->type & 1) ? -ht / 2 : 0;
186 int ury = (g->type & 2) ? ht : ht / 2;
187 wb_bbox(wb, 0, lly, urx, ury);
190 wb->h += g ? font_gwid(g->font, dev_font(wb->f), wb->s, g->wid) : 0;
191 wb->ct |= g ? g->type : 0;
192 wb_stsb(wb);
196 /* return nonzero if it cannot be hyphenated */
197 static int wb_hyph(char src[][GNLEN], int src_n, char *src_hyph, int flg)
199 char word[WORDLEN * GNLEN]; /* word to pass to hyphenate() */
200 char hyph[WORDLEN * GNLEN]; /* hyphenation points of word */
201 int smap[WORDLEN]; /* the mapping from src[] to word[] */
202 char *s, *d;
203 int i;
204 d = word;
205 *d = '\0';
206 for (i = 0; i < src_n; i++) {
207 s = src[i];
208 smap[i] = d - word;
209 if (c_hystop(s))
210 return 1;
211 if (c_hymark(s))
212 continue;
213 d += hy_cput(d, s);
215 memset(hyph, 0, (d - word) * sizeof(hyph[0]));
216 hyphenate(hyph, word, flg);
217 for (i = 0; i < src_n; i++)
218 src_hyph[i] = hyph[smap[i]];
219 return 0;
222 static int wb_collect(struct wb *wb, int val)
224 int old = wb->sub_collect;
225 wb->sub_collect = val;
226 return old;
229 /* output the collected characters; only for those present in wb->f font */
230 static void wb_flushsub(struct wb *wb)
232 struct font *fn;
233 struct glyph *gsrc[WORDLEN];
234 struct glyph *gdst[WORDLEN];
235 int x[WORDLEN], y[WORDLEN], xadv[WORDLEN], yadv[WORDLEN];
236 int dmap[WORDLEN];
237 char src_hyph[WORDLEN];
238 int dst_n, i;
239 int sidx = 0;
240 if (!wb->sub_n || !wb->sub_collect)
241 return;
242 wb->sub_collect = 0;
243 fn = dev_font(wb->f);
244 if (!n_hy || wb_hyph(wb->sub_c, wb->sub_n, src_hyph, n_hy))
245 memset(src_hyph, 0, sizeof(src_hyph));
246 /* call font_layout() for collected glyphs; skip hyphenation marks */
247 while (sidx < wb->sub_n) {
248 int beg = sidx;
249 for (; sidx < wb->sub_n && !c_hymark(wb->sub_c[sidx]); sidx++)
250 gsrc[sidx - beg] = font_find(fn, wb->sub_c[sidx]);
251 dst_n = font_layout(fn, gsrc, sidx - beg, wb->s,
252 gdst, dmap, x, y, xadv, yadv, n_lg, n_kn);
253 for (i = 0; i < dst_n; i++) {
254 int xd[2] = {x[i], xadv[i] - x[i]};
255 int yd[2] = {y[i], yadv[i] - y[i]};
256 if (xd[wb->cd])
257 wb_hmov(wb, font_wid(fn, wb->s, xd[wb->cd]));
258 if (yd[wb->cd])
259 wb_vmov(wb, font_wid(fn, wb->s, yd[wb->cd]));
260 if (src_hyph[beg + dmap[i]])
261 wb_putbuf(wb, c_hc);
262 if (gdst[i] == gsrc[dmap[i]])
263 wb_putbuf(wb, wb->sub_c[beg + dmap[i]]);
264 else
265 wb_putbuf(wb, gdst[i]->name);
266 if (xd[1 - wb->cd])
267 wb_hmov(wb, font_wid(fn, wb->s, xd[1 - wb->cd]));
268 if (yd[1 - wb->cd])
269 wb_vmov(wb, font_wid(fn, wb->s, yd[1 - wb->cd]));
271 for (; sidx < wb->sub_n && c_hymark(wb->sub_c[sidx]); sidx++)
272 wb_putbuf(wb, wb->sub_c[sidx]);
274 wb->sub_n = 0;
275 wb->icleft = 0;
276 wb->sub_collect = 1;
279 void wb_put(struct wb *wb, char *c)
281 if (c[0] == '\n') {
282 wb->part = 0;
283 return;
285 if (wb_pendingdir(wb))
286 wb_flushdir(wb);
287 if (c[0] == ' ') {
288 wb_flushsub(wb);
289 wb_hmov(wb, font_swid(dev_font(R_F(wb)), R_S(wb), n_ss));
290 return;
292 if (!strcmp(c_nb, c)) {
293 wb_flushsub(wb);
294 sbuf_append(&wb->sbuf, c);
295 wb->h += font_swid(dev_font(R_F(wb)), R_S(wb), n_ss);
296 return;
298 if (wb_pendingfont(wb) || wb->sub_n == LEN(wb->sub_c))
299 wb_flush(wb);
300 if (wb->sub_collect) {
301 if (font_find(dev_font(wb->f), c) || c_hymark(c))
302 strcpy(wb->sub_c[wb->sub_n++], c);
303 else
304 wb_putraw(wb, c);
305 } else {
306 wb_putbuf(wb, c);
310 /* just like wb_put() but disable subword collection */
311 void wb_putraw(struct wb *wb, char *c)
313 int collect;
314 wb_flushsub(wb);
315 collect = wb_collect(wb, 0);
316 wb_put(wb, c);
317 wb_collect(wb, collect);
320 /* just like wb_put(), but call cdef_expand() if c is defined */
321 void wb_putexpand(struct wb *wb, char *c)
323 if (cdef_expand(wb, c, R_F(wb)))
324 wb_put(wb, c);
327 int wb_part(struct wb *wb)
329 return wb->part;
332 void wb_setpart(struct wb *wb)
334 wb->part = 1;
337 int wb_cost(struct wb *wb)
339 return wb->cost;
342 void wb_setcost(struct wb *wb, int cost)
344 wb->cost = cost;
347 void wb_drawl(struct wb *wb, int c, int h, int v)
349 wb_flush(wb);
350 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
351 wb->h += h;
352 wb->v += v;
353 wb_stsb(wb);
356 void wb_drawc(struct wb *wb, int c, int r)
358 wb_flush(wb);
359 sbuf_printf(&wb->sbuf, "%cD'%c %du'", c_ec, c, r);
360 wb->h += r;
363 void wb_drawe(struct wb *wb, int c, int h, int v)
365 wb_flush(wb);
366 sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
367 wb->h += h;
370 void wb_drawa(struct wb *wb, int c, int h1, int v1, int h2, int v2)
372 wb_flush(wb);
373 sbuf_printf(&wb->sbuf, "%cD'%c %du %du %du %du'",
374 c_ec, c, h1, v1, h2, v2);
375 wb->h += h1 + h2;
376 wb->v += v1 + v2;
377 wb_stsb(wb);
380 void wb_drawxbeg(struct wb *wb, int c)
382 wb_flush(wb);
383 sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
386 void wb_drawxdot(struct wb *wb, int h, int v)
388 sbuf_printf(&wb->sbuf, " %du %du", h, v);
389 wb->h += h;
390 wb->v += v;
391 wb_stsb(wb);
394 void wb_drawxcmd(struct wb *wb, char *cmd)
396 sbuf_printf(&wb->sbuf, " %s", cmd);
399 void wb_drawxend(struct wb *wb)
401 sbuf_printf(&wb->sbuf, "'");
404 void wb_reset(struct wb *wb)
406 wb_done(wb);
407 wb_init(wb);
410 char *wb_buf(struct wb *wb)
412 wb_flushsub(wb);
413 return sbuf_buf(&wb->sbuf);
416 static void wb_putc(struct wb *wb, int t, char *s)
418 if (t && t != 'C')
419 wb_flushsub(wb);
420 switch (t) {
421 case 0:
422 case 'C':
423 wb_put(wb, s);
424 break;
425 case 'D':
426 ren_dcmd(wb, s);
427 break;
428 case 'f':
429 wb->r_f = atoi(s);
430 break;
431 case 'h':
432 wb_hmov(wb, atoi(s));
433 break;
434 case 'm':
435 wb->r_m = clr_get(s);
436 break;
437 case 's':
438 wb->r_s = atoi(s);
439 break;
440 case 'v':
441 wb_vmov(wb, atoi(s));
442 break;
443 case 'x':
444 wb_els(wb, atoi(s));
445 break;
446 case 'X':
447 wb_etc(wb, s);
448 break;
449 case '<':
450 case '>':
451 wb->r_cd = t == '<';
452 wb_flushdir(wb);
453 break;
457 void wb_cat(struct wb *wb, struct wb *src)
459 char *s, *d;
460 int c, part;
461 int collect;
462 wb_flushsub(src);
463 wb_flushsub(wb);
464 collect = wb_collect(wb, 0);
465 s = sbuf_buf(&src->sbuf);
466 while ((c = escread(&s, &d)) >= 0)
467 wb_putc(wb, c, d);
468 part = src->part;
469 wb->r_s = -1;
470 wb->r_f = -1;
471 wb->r_m = -1;
472 wb->r_cd = -1;
473 wb_reset(src);
474 src->part = part;
475 wb_collect(wb, collect);
478 int wb_wid(struct wb *wb)
480 wb_flushsub(wb);
481 return wb->h;
484 int wb_hpos(struct wb *wb)
486 wb_flushsub(wb);
487 return wb->h;
490 int wb_vpos(struct wb *wb)
492 wb_flushsub(wb);
493 return wb->v;
496 int wb_empty(struct wb *wb)
498 return !wb->sub_n && sbuf_empty(&wb->sbuf);
501 /* return 1 if wb ends a sentence (.?!) */
502 int wb_eos(struct wb *wb)
504 int i = wb->sub_n - 1;
505 while (i > 0 && c_eostran(wb->sub_c[i]))
506 i--;
507 return i >= 0 && c_eossent(wb->sub_c[i]);
510 void wb_wconf(struct wb *wb, int *ct, int *st, int *sb,
511 int *llx, int *lly, int *urx, int *ury)
513 wb_flushsub(wb);
514 *ct = wb->ct;
515 *st = -wb->st;
516 *sb = -wb->sb;
517 *llx = wb->llx < BBMAX ? wb->llx : 0;
518 *lly = wb->lly < BBMAX ? -wb->lly : 0;
519 *urx = wb->urx > BBMIN ? wb->urx : 0;
520 *ury = wb->ury > BBMIN ? -wb->ury : 0;
523 static struct glyph *wb_prevglyph(struct wb *wb)
525 return wb->sub_n ? dev_glyph(wb->sub_c[wb->sub_n - 1], wb->f) : NULL;
528 void wb_italiccorrection(struct wb *wb)
530 struct glyph *g = wb_prevglyph(wb);
531 if (g && glyph_ic(g))
532 wb_hmov(wb, font_wid(g->font, wb->s, glyph_ic(g)));
535 void wb_italiccorrectionleft(struct wb *wb)
537 wb_flushsub(wb);
538 wb->icleft = 1;
541 void wb_fnszget(struct wb *wb, int *fn, int *sz, int *m, int *cd)
543 wb_flushsub(wb);
544 *fn = wb->r_f;
545 *sz = wb->r_s;
546 *m = wb->r_m;
547 *cd = wb->r_cd;
550 void wb_fnszset(struct wb *wb, int fn, int sz, int m, int cd)
552 wb->r_f = fn;
553 wb->r_s = sz;
554 wb->r_m = m;
555 wb->r_cd = cd;
558 void wb_catstr(struct wb *wb, char *s, char *end)
560 int collect, c;
561 char *d;
562 wb_flushsub(wb);
563 collect = wb_collect(wb, 0);
564 while (s < end && (c = escread(&s, &d)) >= 0)
565 wb_putc(wb, c, d);
566 wb_collect(wb, collect);
569 /* return the size of \(hy if appended to wb */
570 int wb_hywid(struct wb *wb)
572 struct glyph *g = dev_glyph("hy", wb->f);
573 return g ? font_gwid(g->font, dev_font(R_F(wb)), R_S(wb), g->wid) : 0;
576 /* return the size of space if appended to wb */
577 int wb_swid(struct wb *wb)
579 return font_swid(dev_font(R_F(wb)), R_S(wb), n_ss);
582 static char *keshideh_chars[] = {
583 "ﺒ", "ﺑ", "ﭙ", "ﭘ", "ﺘ", "ﺗ", "ﺜ", "ﺛ", "ﺴ", "ﺳ",
584 "ﺸ", "ﺷ", "ﻔ", "ﻓ", "ﻘ", "ﻗ", "ﮑ", "ﮐ", "ﮕ", "ﮔ",
585 "ﻤ", "ﻣ", "ﻨ", "ﻧ", "ﻬ", "ﻫ", "ﯿ", "ﯾ",
588 static int keshideh(char *c)
590 int i;
591 for (i = 0; i < LEN(keshideh_chars); i++)
592 if (!strcmp(keshideh_chars[i], c))
593 return 1;
594 return 0;
597 /* insert keshideh */
598 int wb_keshideh(char *word, struct wb *dst, int wid)
600 char p[GNLEN] = "";
601 char *s, *d, *s_prev = NULL, *s_kesh = NULL;
602 int ins = 0;
603 int c;
604 /* find the last keshideh position */
605 s = word;
606 while ((c = escread(&s, &d)) >= 0) {
607 wb_putc(dst, c, d);
608 if (!c && keshideh(p)) {
609 struct glyph *g = dev_glyph("ـ", R_F(dst));
610 int kw = g ? font_gwid(g->font,
611 dev_font(R_F(dst)), R_S(dst), g->wid) : 0;
612 if (g && kw < wid) {
613 s_kesh = s_prev;
614 ins = kw;
617 s_prev = s;
618 strcpy(p, c ? "" : d);
620 /* insert the keshideh at s_kesh */
621 s = word;
622 wb_reset(dst);
623 while ((c = escread(&s, &d)) >= 0) {
624 wb_putc(dst, c, d);
625 if (s == s_kesh)
626 wb_putc(dst, 0, "ـ");
628 return ins;