wb: report the bounding box for \w
[neatroff.git] / adj.c
blobe989bd319bc5467596e55c2de40d70065912f398
1 /* adjustment buffer for putting words into lines */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include "roff.h"
7 #define ADJ_LLEN(a) MAX(0, (a)->ll - ((a)->lt >= 0 ? (a)->lt : (a)->li))
9 struct adj {
10 struct wb wbs[NWORDS]; /* words in buf */
11 int gaps[NWORDS]; /* gaps before words */
12 int nwords;
13 int wid; /* total width of buf */
14 int swid; /* current space width */
15 int gap; /* space before the next word */
16 int nls; /* newlines before the next word */
17 int l, i, t; /* current .l, .i and ti */
18 int ll, li, lt; /* current line's .l, .i and ti */
19 int filled; /* filled all words in the last adj_fill() */
22 void adj_ll(struct adj *adj, int ll)
24 adj->l = ll;
27 void adj_ti(struct adj *adj, int ti)
29 adj->t = ti;
32 void adj_in(struct adj *adj, int in)
34 adj->i = in;
37 /* .ll, .in and .ti are delayed until the partial line is output */
38 static void adj_confupdate(struct adj *adj)
40 adj->ll = adj->l;
41 adj->li = adj->i;
42 adj->lt = adj->t;
43 adj->t = -1;
46 /* does the adjustment buffer need to be flushed without filling? */
47 static int adj_fullnf(struct adj *a)
49 /* blank lines; indented lines; newlines when buffer is empty */
50 return a->nls > 1 || (a->nls && a->gap) ||
51 (a->nls - a->filled > 0 && !a->nwords);
54 /* does the adjustment buffer need to be flushed? */
55 int adj_full(struct adj *a, int fill)
57 if (!fill)
58 return a->nls - a->filled > 0;
59 if (adj_fullnf(a))
60 return 1;
61 return a->nwords && a->wid > ADJ_LLEN(a);
64 /* is the adjustment buffer empty? */
65 int adj_empty(struct adj *a, int fill)
67 return !fill ? a->nls - a->filled <= 0 : !a->nwords && !adj_fullnf(a);
70 /* set space width */
71 void adj_swid(struct adj *adj, int swid)
73 adj->swid = swid;
76 /* move words inside an adj struct */
77 static void adj_movewords(struct adj *a, int dst, int src, int len)
79 memmove(a->wbs + dst, a->wbs + src, len * sizeof(a->wbs[0]));
80 memmove(a->gaps + dst, a->gaps + src, len * sizeof(a->gaps[0]));
83 static int adj_linewid(struct adj *a, int n)
85 int i, w = 0;
86 for (i = 0; i < n; i++)
87 w += wb_wid(&a->wbs[i]) + a->gaps[i];
88 return w;
91 static int adj_linefit(struct adj *a, int llen)
93 int i, w = 0;
94 for (i = 0; i < a->nwords; i++) {
95 w += wb_wid(&a->wbs[i]) + a->gaps[i];
96 if (w > llen)
97 return i;
99 return i;
102 /* move n words from the adjustment buffer to s */
103 static int adj_move(struct adj *a, int n, struct sbuf *s, int *els_neg, int *els_pos)
105 struct wb *cur;
106 int w = 0;
107 int i;
108 *els_neg = 0;
109 *els_pos = 0;
110 for (i = 0; i < n; i++) {
111 cur = &a->wbs[i];
112 sbuf_printf(s, "%ch'%du'", c_ec, a->gaps[i]);
113 sbuf_append(s, sbuf_buf(&cur->sbuf));
114 w += wb_wid(cur) + a->gaps[i];
115 if (cur->els_neg < *els_neg)
116 *els_neg = cur->els_neg;
117 if (cur->els_pos > *els_pos)
118 *els_pos = cur->els_pos;
119 wb_done(cur);
121 if (!n)
122 return 0;
123 a->nwords -= n;
124 adj_movewords(a, 0, n, a->nwords);
125 a->wid = adj_linewid(a, a->nwords);
126 if (a->nwords) /* apply the new .l and .i */
127 adj_confupdate(a);
128 return w;
131 /* try to hyphenate the n-th word */
132 static void adj_hyph(struct adj *a, int n, int w, int hyph)
134 struct wb w1, w2;
135 int flg = hyph | (n ? 0 : HY_ANY);
136 wb_init(&w1);
137 wb_init(&w2);
138 if (wb_hyph(&a->wbs[n], w, &w1, &w2, flg)) {
139 wb_done(&w1);
140 wb_done(&w2);
141 return;
143 adj_movewords(a, n + 2, n + 1, a->nwords - n);
144 wb_done(&a->wbs[n]);
145 memcpy(&a->wbs[n], &w1, sizeof(w1));
146 memcpy(&a->wbs[n + 1], &w2, sizeof(w2));
147 a->nwords++;
148 a->gaps[n + 1] = 0;
149 a->wid = adj_linewid(a, a->nwords);
152 /* fill and copy a line into s */
153 int adj_fill(struct adj *a, int ad_b, int fill, int hyph, struct sbuf *s,
154 int *ll, int *in, int *ti, int *els_neg, int *els_pos)
156 int adj_div, adj_rem;
157 int w = 0;
158 int i, n;
159 int llen = ADJ_LLEN(a);
160 *ll = a->ll;
161 *in = a->li;
162 *ti = a->lt;
163 if (!fill || adj_fullnf(a)) {
164 a->filled = 0;
165 a->nls--;
166 return adj_move(a, a->nwords, s, els_neg, els_pos);
168 n = adj_linefit(a, llen);
169 if (n < a->nwords)
170 adj_hyph(a, n, llen - adj_linewid(a, n) - a->gaps[n], hyph);
171 n = adj_linefit(a, llen);
172 if (!n && a->nwords)
173 n = 1;
174 w = adj_linewid(a, n);
175 if (ad_b && n > 1) {
176 adj_div = (llen - w) / (n - 1);
177 adj_rem = (llen - w) % (n - 1);
178 for (i = 0; i < n - 1; i++)
179 a->gaps[i + 1] += adj_div + (i < adj_rem);
181 w = adj_move(a, n, s, els_neg, els_pos);
182 if (a->nwords)
183 a->wid -= a->gaps[0];
184 a->gaps[0] = 0;
185 a->filled = n && !a->nwords;
186 return w;
189 void adj_sp(struct adj *adj)
191 adj->gap += adj->swid;
194 void adj_nl(struct adj *adj)
196 adj->nls++;
197 adj->gap = 0;
200 /* ignore the previous newline */
201 void adj_nonl(struct adj *adj)
203 if (adj->nls)
204 adj->gap += adj->swid;
205 adj->nls = 0;
208 static void adj_word(struct adj *adj, struct wb *wb)
210 int i = adj->nwords++;
211 wb_init(&adj->wbs[i]);
212 adj->gaps[i] = adj->filled ? 0 : adj->gap;
213 adj->filled = 0;
214 adj->wid += wb_wid(wb) + adj->gap;
215 wb_cat(&adj->wbs[i], wb);
218 static int adj_eos(struct adj *adj)
220 return adj->nwords && wb_eos(&adj->wbs[adj->nwords - 1]);
223 /* insert wb into the adjustment buffer */
224 void adj_wb(struct adj *adj, struct wb *wb)
226 if (wb_empty(wb) || adj->nwords == NWORDS)
227 return;
228 if (!adj->nwords) /* apply the new .l and .i */
229 adj_confupdate(adj);
230 if (adj->nls && !adj->gap && adj->nwords >= 1)
231 adj->gap = adj_eos(adj) ? adj->swid * 2 : adj->swid;
232 adj_word(adj, wb);
233 adj->nls = 0;
234 adj->gap = 0;
237 struct adj *adj_alloc(void)
239 struct adj *adj = malloc(sizeof(*adj));
240 memset(adj, 0, sizeof(*adj));
241 return adj;
244 void adj_free(struct adj *adj)
246 free(adj);
249 int adj_wid(struct adj *adj)
251 return adj->wid + (adj->nls ? adj->swid : adj->gap);