Annotation of mandoc/out.c, Revision 1.68
1.68 ! schwarze 1: /* $Id: out.c,v 1.67 2017/06/12 22:05:57 schwarze Exp $ */
1.1 kristaps 2: /*
1.36 schwarze 3: * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.63 schwarze 4: * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
1.12 kristaps 18: #include "config.h"
19:
1.1 kristaps 20: #include <sys/types.h>
21:
1.6 kristaps 22: #include <assert.h>
1.1 kristaps 23: #include <stdlib.h>
1.6 kristaps 24: #include <string.h>
1.7 kristaps 25: #include <time.h>
1.1 kristaps 26:
1.47 schwarze 27: #include "mandoc_aux.h"
1.30 kristaps 28: #include "mandoc.h"
1.1 kristaps 29: #include "out.h"
30:
1.30 kristaps 31: static void tblcalc_data(struct rofftbl *, struct roffcol *,
1.65 schwarze 32: const struct tbl_opts *, const struct tbl_dat *,
33: size_t);
1.30 kristaps 34: static void tblcalc_literal(struct rofftbl *, struct roffcol *,
1.65 schwarze 35: const struct tbl_dat *, size_t);
1.30 kristaps 36: static void tblcalc_number(struct rofftbl *, struct roffcol *,
1.45 schwarze 37: const struct tbl_opts *, const struct tbl_dat *);
1.30 kristaps 38:
1.48 schwarze 39:
40: /*
1.55 schwarze 41: * Parse the *src string and store a scaling unit into *dst.
42: * If the string doesn't specify the unit, use the default.
43: * If no default is specified, fail.
1.64 schwarze 44: * Return a pointer to the byte after the last byte used,
45: * or NULL on total failure.
1.3 kristaps 46: */
1.64 schwarze 47: const char *
1.5 kristaps 48: a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
1.1 kristaps 49: {
1.55 schwarze 50: char *endptr;
1.1 kristaps 51:
1.56 schwarze 52: dst->unit = def == SCALE_MAX ? SCALE_BU : def;
53: dst->scale = strtod(src, &endptr);
54: if (endptr == src)
1.64 schwarze 55: return NULL;
1.5 kristaps 56:
1.56 schwarze 57: switch (*endptr++) {
1.48 schwarze 58: case 'c':
1.56 schwarze 59: dst->unit = SCALE_CM;
1.3 kristaps 60: break;
1.48 schwarze 61: case 'i':
1.56 schwarze 62: dst->unit = SCALE_IN;
63: break;
64: case 'f':
65: dst->unit = SCALE_FS;
66: break;
67: case 'M':
68: dst->unit = SCALE_MM;
69: break;
70: case 'm':
71: dst->unit = SCALE_EM;
72: break;
73: case 'n':
74: dst->unit = SCALE_EN;
1.3 kristaps 75: break;
1.48 schwarze 76: case 'P':
1.56 schwarze 77: dst->unit = SCALE_PC;
1.3 kristaps 78: break;
1.48 schwarze 79: case 'p':
1.56 schwarze 80: dst->unit = SCALE_PT;
1.3 kristaps 81: break;
1.56 schwarze 82: case 'u':
83: dst->unit = SCALE_BU;
1.3 kristaps 84: break;
1.48 schwarze 85: case 'v':
1.56 schwarze 86: dst->unit = SCALE_VS;
1.3 kristaps 87: break;
1.68 ! schwarze 88: default:
1.56 schwarze 89: endptr--;
1.5 kristaps 90: if (SCALE_MAX == def)
1.64 schwarze 91: return NULL;
1.56 schwarze 92: dst->unit = def;
1.3 kristaps 93: break;
94: }
1.64 schwarze 95: return endptr;
1.8 kristaps 96: }
1.30 kristaps 97:
98: /*
99: * Calculate the abstract widths and decimal positions of columns in a
100: * table. This routine allocates the columns structures then runs over
101: * all rows and cells in the table. The function pointers in "tbl" are
102: * used for the actual width calculations.
103: */
104: void
1.52 schwarze 105: tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
1.66 schwarze 106: size_t offset, size_t rmargin)
1.30 kristaps 107: {
1.65 schwarze 108: struct roffsu su;
1.58 schwarze 109: const struct tbl_opts *opts;
1.30 kristaps 110: const struct tbl_dat *dp;
111: struct roffcol *col;
1.52 schwarze 112: size_t ewidth, xwidth;
1.43 schwarze 113: int spans;
1.58 schwarze 114: int icol, maxcol, necol, nxcol, quirkcol;
1.30 kristaps 115:
116: /*
117: * Allocate the master column specifiers. These will hold the
118: * widths and decimal positions for all cells in the column. It
119: * must be freed and nullified by the caller.
120: */
121:
122: assert(NULL == tbl->cols);
1.48 schwarze 123: tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
124: sizeof(struct roffcol));
1.58 schwarze 125: opts = sp->opts;
1.30 kristaps 126:
1.53 schwarze 127: for (maxcol = -1; sp; sp = sp->next) {
1.30 kristaps 128: if (TBL_SPAN_DATA != sp->pos)
129: continue;
1.43 schwarze 130: spans = 1;
1.30 kristaps 131: /*
132: * Account for the data cells in the layout, matching it
133: * to data cells in the data section.
134: */
135: for (dp = sp->first; dp; dp = dp->next) {
1.43 schwarze 136: /* Do not used spanned cells in the calculation. */
137: if (0 < --spans)
138: continue;
139: spans = dp->spans;
140: if (1 < spans)
141: continue;
1.59 schwarze 142: icol = dp->layout->col;
1.52 schwarze 143: if (maxcol < icol)
144: maxcol = icol;
145: col = tbl->cols + icol;
146: col->flags |= dp->layout->flags;
147: if (dp->layout->flags & TBL_CELL_WIGN)
148: continue;
1.65 schwarze 149: if (dp->layout->wstr != NULL &&
150: dp->layout->width == 0 &&
151: a2roffsu(dp->layout->wstr, &su, SCALE_EN)
152: != NULL)
153: dp->layout->width =
154: (*tbl->sulen)(&su, tbl->arg);
155: if (col->width < dp->layout->width)
156: col->width = dp->layout->width;
1.66 schwarze 157: tblcalc_data(tbl, col, opts, dp,
1.67 schwarze 158: dp->block == 0 ? 0 :
159: dp->layout->width ? dp->layout->width :
160: rmargin ? rmargin / (sp->opts->cols + 1) : 0);
1.52 schwarze 161: }
162: }
163:
164: /*
165: * Count columns to equalize and columns to maximize.
166: * Find maximum width of the columns to equalize.
167: * Find total width of the columns *not* to maximize.
168: */
169:
170: necol = nxcol = 0;
171: ewidth = xwidth = 0;
172: for (icol = 0; icol <= maxcol; icol++) {
173: col = tbl->cols + icol;
174: if (col->flags & TBL_CELL_EQUAL) {
175: necol++;
176: if (ewidth < col->width)
177: ewidth = col->width;
178: }
179: if (col->flags & TBL_CELL_WMAX)
180: nxcol++;
181: else
182: xwidth += col->width;
183: }
184:
185: /*
186: * Equalize columns, if requested for any of them.
187: * Update total width of the columns not to maximize.
188: */
189:
190: if (necol) {
191: for (icol = 0; icol <= maxcol; icol++) {
192: col = tbl->cols + icol;
193: if ( ! (col->flags & TBL_CELL_EQUAL))
194: continue;
195: if (col->width == ewidth)
196: continue;
1.66 schwarze 197: if (nxcol && rmargin)
1.52 schwarze 198: xwidth += ewidth - col->width;
199: col->width = ewidth;
200: }
201: }
202:
203: /*
204: * If there are any columns to maximize, find the total
205: * available width, deducting 3n margins between columns.
206: * Distribute the available width evenly.
207: */
208:
1.66 schwarze 209: if (nxcol && rmargin) {
1.63 schwarze 210: xwidth += 3*maxcol +
1.58 schwarze 211: (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
212: 2 : !!opts->lvert + !!opts->rvert);
1.66 schwarze 213: if (rmargin <= offset + xwidth)
1.63 schwarze 214: return;
1.66 schwarze 215: xwidth = rmargin - offset - xwidth;
1.58 schwarze 216:
217: /*
218: * Emulate a bug in GNU tbl width calculation that
219: * manifests itself for large numbers of x-columns.
220: * Emulating it for 5 x-columns gives identical
221: * behaviour for up to 6 x-columns.
222: */
223:
224: if (nxcol == 5) {
225: quirkcol = xwidth % nxcol + 2;
226: if (quirkcol != 3 && quirkcol != 4)
227: quirkcol = -1;
228: } else
229: quirkcol = -1;
230:
231: necol = 0;
232: ewidth = 0;
1.52 schwarze 233: for (icol = 0; icol <= maxcol; icol++) {
234: col = tbl->cols + icol;
235: if ( ! (col->flags & TBL_CELL_WMAX))
236: continue;
1.58 schwarze 237: col->width = (double)xwidth * ++necol / nxcol
238: - ewidth + 0.4995;
239: if (necol == quirkcol)
240: col->width--;
241: ewidth += col->width;
1.30 kristaps 242: }
243: }
244: }
245:
246: static void
247: tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
1.65 schwarze 248: const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
1.30 kristaps 249: {
250: size_t sz;
251:
252: /* Branch down into data sub-types. */
253:
254: switch (dp->layout->pos) {
1.48 schwarze 255: case TBL_CELL_HORIZ:
256: case TBL_CELL_DHORIZ:
1.30 kristaps 257: sz = (*tbl->len)(1, tbl->arg);
258: if (col->width < sz)
259: col->width = sz;
260: break;
1.48 schwarze 261: case TBL_CELL_LONG:
262: case TBL_CELL_CENTRE:
263: case TBL_CELL_LEFT:
264: case TBL_CELL_RIGHT:
1.65 schwarze 265: tblcalc_literal(tbl, col, dp, mw);
1.30 kristaps 266: break;
1.48 schwarze 267: case TBL_CELL_NUMBER:
1.45 schwarze 268: tblcalc_number(tbl, col, opts, dp);
1.35 kristaps 269: break;
1.48 schwarze 270: case TBL_CELL_DOWN:
1.30 kristaps 271: break;
272: default:
273: abort();
274: }
275: }
276:
277: static void
278: tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
1.65 schwarze 279: const struct tbl_dat *dp, size_t mw)
1.30 kristaps 280: {
1.65 schwarze 281: const char *str; /* Beginning of the first line. */
282: const char *beg; /* Beginning of the current line. */
283: char *end; /* End of the current line. */
1.66 schwarze 284: size_t lsz; /* Length of the current line. */
285: size_t wsz; /* Length of the current word. */
1.65 schwarze 286:
287: if (dp->string == NULL || *dp->string == '\0')
288: return;
289: str = mw ? mandoc_strdup(dp->string) : dp->string;
1.66 schwarze 290: lsz = 0;
1.65 schwarze 291: for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
292: end = mw ? strchr(beg, ' ') : NULL;
293: if (end != NULL) {
294: *end++ = '\0';
295: while (*end == ' ')
296: end++;
297: }
1.66 schwarze 298: wsz = (*tbl->slen)(beg, tbl->arg);
299: if (mw && lsz && lsz + 1 + wsz <= mw)
300: lsz += 1 + wsz;
301: else
302: lsz = wsz;
303: if (col->width < lsz)
304: col->width = lsz;
1.65 schwarze 305: }
306: if (mw)
307: free((void *)str);
1.30 kristaps 308: }
309:
310: static void
311: tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
1.45 schwarze 312: const struct tbl_opts *opts, const struct tbl_dat *dp)
1.30 kristaps 313: {
1.48 schwarze 314: int i;
1.34 kristaps 315: size_t sz, psz, ssz, d;
316: const char *str;
1.30 kristaps 317: char *cp;
318: char buf[2];
319:
320: /*
321: * First calculate number width and decimal place (last + 1 for
1.43 schwarze 322: * non-decimal numbers). If the stored decimal is subsequent to
1.30 kristaps 323: * ours, make our size longer by that difference
324: * (right-"shifting"); similarly, if ours is subsequent the
325: * stored, then extend the stored size by the difference.
326: * Finally, re-assign the stored values.
327: */
328:
1.34 kristaps 329: str = dp->string ? dp->string : "";
330: sz = (*tbl->slen)(str, tbl->arg);
1.30 kristaps 331:
1.34 kristaps 332: /* FIXME: TBL_DATA_HORIZ et al.? */
1.30 kristaps 333:
1.45 schwarze 334: buf[0] = opts->decimal;
1.30 kristaps 335: buf[1] = '\0';
336:
337: psz = (*tbl->slen)(buf, tbl->arg);
338:
1.45 schwarze 339: if (NULL != (cp = strrchr(str, opts->decimal))) {
1.30 kristaps 340: buf[1] = '\0';
341: for (ssz = 0, i = 0; cp != &str[i]; i++) {
342: buf[0] = str[i];
343: ssz += (*tbl->slen)(buf, tbl->arg);
344: }
345: d = ssz + psz;
346: } else
347: d = sz + psz;
348:
349: /* Adjust the settings for this column. */
350:
351: if (col->decimal > d) {
352: sz += col->decimal - d;
353: d = col->decimal;
354: } else
355: col->width += d - col->decimal;
356:
357: if (sz > col->width)
358: col->width = sz;
359: if (d > col->decimal)
360: col->decimal = d;
361: }
CVSweb