Annotation of mandoc/tbl_term.c, Revision 1.30
1.30 ! schwarze 1: /* $Id: tbl_term.c,v 1.29 2014/10/13 23:31:46 schwarze Exp $ */
1.1 kristaps 2: /*
1.20 kristaps 3: * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.26 schwarze 4: * Copyright (c) 2011, 2012, 2014 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: */
18: #include "config.h"
1.28 schwarze 19:
20: #include <sys/types.h>
1.1 kristaps 21:
22: #include <assert.h>
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26:
27: #include "mandoc.h"
28: #include "out.h"
29: #include "term.h"
30:
1.11 kristaps 31: static size_t term_tbl_len(size_t, void *);
32: static size_t term_tbl_strlen(const char *, void *);
1.12 kristaps 33: static void tbl_char(struct termp *, char, size_t);
1.25 schwarze 34: static void tbl_data(struct termp *, const struct tbl_opts *,
1.27 schwarze 35: const struct tbl_dat *,
1.11 kristaps 36: const struct roffcol *);
1.21 schwarze 37: static size_t tbl_rulewidth(struct termp *, const struct tbl_head *);
38: static void tbl_hframe(struct termp *, const struct tbl_span *, int);
1.27 schwarze 39: static void tbl_literal(struct termp *, const struct tbl_dat *,
1.11 kristaps 40: const struct roffcol *);
1.27 schwarze 41: static void tbl_number(struct termp *, const struct tbl_opts *,
42: const struct tbl_dat *,
1.11 kristaps 43: const struct roffcol *);
44: static void tbl_hrule(struct termp *, const struct tbl_span *);
45: static void tbl_vrule(struct termp *, const struct tbl_head *);
1.29 schwarze 46: static void tbl_word(struct termp *, const struct tbl_dat *);
1.11 kristaps 47:
48:
49: static size_t
50: term_tbl_strlen(const char *p, void *arg)
51: {
52:
53: return(term_strlen((const struct termp *)arg, p));
54: }
55:
56: static size_t
57: term_tbl_len(size_t sz, void *arg)
58: {
59:
60: return(term_len((const struct termp *)arg, sz));
61: }
1.1 kristaps 62:
63: void
64: term_tbl(struct termp *tp, const struct tbl_span *sp)
65: {
1.11 kristaps 66: const struct tbl_head *hp;
67: const struct tbl_dat *dp;
68: struct roffcol *col;
1.16 kristaps 69: int spans;
1.27 schwarze 70: size_t rmargin, maxrmargin;
1.11 kristaps 71:
72: rmargin = tp->rmargin;
73: maxrmargin = tp->maxrmargin;
74:
75: tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
1.1 kristaps 76:
1.4 kristaps 77: /* Inhibit printing of spaces: we do padding ourselves. */
78:
79: tp->flags |= TERMP_NONOSPACE;
80: tp->flags |= TERMP_NOSPACE;
81:
82: /*
1.11 kristaps 83: * The first time we're invoked for a given table block,
84: * calculate the table widths and decimal positions.
1.4 kristaps 85: */
86:
1.2 kristaps 87: if (TBL_SPAN_FIRST & sp->flags) {
1.11 kristaps 88: term_flushln(tp);
89:
90: tp->tbl.len = term_tbl_len;
91: tp->tbl.slen = term_tbl_strlen;
92: tp->tbl.arg = tp;
1.4 kristaps 93:
1.30 ! schwarze 94: tblcalc(&tp->tbl, sp, rmargin - tp->offset);
1.2 kristaps 95: }
1.1 kristaps 96:
1.4 kristaps 97: /* Horizontal frame at the start of boxed tables. */
98:
1.21 schwarze 99: if (TBL_SPAN_FIRST & sp->flags) {
1.25 schwarze 100: if (TBL_OPT_DBOX & sp->opts->opts)
1.21 schwarze 101: tbl_hframe(tp, sp, 1);
1.25 schwarze 102: if (TBL_OPT_DBOX & sp->opts->opts ||
103: TBL_OPT_BOX & sp->opts->opts)
1.21 schwarze 104: tbl_hframe(tp, sp, 0);
105: }
1.1 kristaps 106:
1.4 kristaps 107: /* Vertical frame at the start of each row. */
1.1 kristaps 108:
1.26 schwarze 109: if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts ||
110: sp->head->vert)
1.21 schwarze 111: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
1.27 schwarze 112: TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
1.1 kristaps 113:
1.4 kristaps 114: /*
115: * Now print the actual data itself depending on the span type.
116: * Spanner spans get a horizontal rule; data spanners have their
117: * data printed by matching data to header.
118: */
119:
1.1 kristaps 120: switch (sp->pos) {
1.27 schwarze 121: case TBL_SPAN_HORIZ:
1.1 kristaps 122: /* FALLTHROUGH */
1.27 schwarze 123: case TBL_SPAN_DHORIZ:
1.1 kristaps 124: tbl_hrule(tp, sp);
125: break;
1.27 schwarze 126: case TBL_SPAN_DATA:
1.4 kristaps 127: /* Iterate over template headers. */
128: dp = sp->first;
1.16 kristaps 129: spans = 0;
1.4 kristaps 130: for (hp = sp->head; hp; hp = hp->next) {
1.22 schwarze 131:
1.27 schwarze 132: /*
1.16 kristaps 133: * If the current data header is invoked during
134: * a spanner ("spans" > 0), don't emit anything
135: * at all.
136: */
1.11 kristaps 137:
1.16 kristaps 138: if (--spans >= 0)
139: continue;
140:
1.22 schwarze 141: /* Separate columns. */
1.21 schwarze 142:
1.22 schwarze 143: if (NULL != hp->prev)
144: tbl_vrule(tp, hp);
1.21 schwarze 145:
1.11 kristaps 146: col = &tp->tbl.cols[hp->ident];
1.25 schwarze 147: tbl_data(tp, sp->opts, dp, col);
1.1 kristaps 148:
1.27 schwarze 149: /*
1.16 kristaps 150: * Go to the next data cell and assign the
151: * number of subsequent spans, if applicable.
152: */
153:
154: if (dp) {
155: spans = dp->spans;
1.1 kristaps 156: dp = dp->next;
1.16 kristaps 157: }
1.1 kristaps 158: }
1.4 kristaps 159: break;
1.1 kristaps 160: }
161:
1.21 schwarze 162: /* Vertical frame at the end of each row. */
163:
1.26 schwarze 164: if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts ||
165: sp->layout->vert)
1.21 schwarze 166: term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
1.27 schwarze 167: TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
1.1 kristaps 168: term_flushln(tp);
169:
1.4 kristaps 170: /*
171: * If we're the last row, clean up after ourselves: clear the
172: * existing table configuration and set it to NULL.
173: */
174:
1.2 kristaps 175: if (TBL_SPAN_LAST & sp->flags) {
1.25 schwarze 176: if (TBL_OPT_DBOX & sp->opts->opts ||
177: TBL_OPT_BOX & sp->opts->opts) {
1.21 schwarze 178: tbl_hframe(tp, sp, 0);
1.24 schwarze 179: tp->skipvsp = 1;
180: }
1.25 schwarze 181: if (TBL_OPT_DBOX & sp->opts->opts) {
1.21 schwarze 182: tbl_hframe(tp, sp, 1);
1.24 schwarze 183: tp->skipvsp = 2;
184: }
1.11 kristaps 185: assert(tp->tbl.cols);
186: free(tp->tbl.cols);
187: tp->tbl.cols = NULL;
1.2 kristaps 188: }
1.1 kristaps 189:
190: tp->flags &= ~TERMP_NONOSPACE;
1.11 kristaps 191: tp->rmargin = rmargin;
192: tp->maxrmargin = maxrmargin;
1.1 kristaps 193:
194: }
195:
1.21 schwarze 196: /*
197: * Horizontal rules extend across the entire table.
198: * Calculate the width by iterating over columns.
199: */
200: static size_t
201: tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
202: {
203: size_t width;
204:
205: width = tp->tbl.cols[hp->ident].width;
1.22 schwarze 206:
207: /* Account for leading blanks. */
208: if (hp->prev)
209: width += 2 - hp->vert;
210:
211: /* Account for trailing blank. */
212: width++;
213:
1.21 schwarze 214: return(width);
215: }
216:
217: /*
218: * Rules inside the table can be single or double
219: * and have crossings with vertical rules marked with pluses.
220: */
1.1 kristaps 221: static void
222: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
223: {
224: const struct tbl_head *hp;
225: char c;
226:
227: c = '-';
228: if (TBL_SPAN_DHORIZ == sp->pos)
229: c = '=';
230:
1.22 schwarze 231: for (hp = sp->head; hp; hp = hp->next) {
232: if (hp->prev && hp->vert)
233: tbl_char(tp, '+', hp->vert);
234: tbl_char(tp, c, tbl_rulewidth(tp, hp));
235: }
1.1 kristaps 236: }
237:
1.21 schwarze 238: /*
239: * Rules above and below the table are always single
240: * and have an additional plus at the beginning and end.
241: * For double frames, this function is called twice,
242: * and the outer one does not have crossings.
243: */
1.1 kristaps 244: static void
1.21 schwarze 245: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1 kristaps 246: {
247: const struct tbl_head *hp;
248:
249: term_word(tp, "+");
1.22 schwarze 250: for (hp = sp->head; hp; hp = hp->next) {
251: if (hp->prev && hp->vert)
252: tbl_char(tp, (outer ? '-' : '+'), hp->vert);
253: tbl_char(tp, '-', tbl_rulewidth(tp, hp));
254: }
1.1 kristaps 255: term_word(tp, "+");
256: term_flushln(tp);
257: }
258:
259: static void
1.25 schwarze 260: tbl_data(struct termp *tp, const struct tbl_opts *opts,
1.27 schwarze 261: const struct tbl_dat *dp,
262: const struct roffcol *col)
1.1 kristaps 263: {
264:
265: if (NULL == dp) {
1.11 kristaps 266: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 267: return;
268: }
1.16 kristaps 269: assert(dp->layout);
1.1 kristaps 270:
271: switch (dp->pos) {
1.27 schwarze 272: case TBL_DATA_NONE:
1.11 kristaps 273: tbl_char(tp, ASCII_NBRSP, col->width);
1.10 kristaps 274: return;
1.27 schwarze 275: case TBL_DATA_HORIZ:
1.1 kristaps 276: /* FALLTHROUGH */
1.27 schwarze 277: case TBL_DATA_NHORIZ:
1.11 kristaps 278: tbl_char(tp, '-', col->width);
1.7 kristaps 279: return;
1.27 schwarze 280: case TBL_DATA_NDHORIZ:
1.7 kristaps 281: /* FALLTHROUGH */
1.27 schwarze 282: case TBL_DATA_DHORIZ:
1.11 kristaps 283: tbl_char(tp, '=', col->width);
1.1 kristaps 284: return;
285: default:
286: break;
287: }
1.27 schwarze 288:
1.16 kristaps 289: switch (dp->layout->pos) {
1.27 schwarze 290: case TBL_CELL_HORIZ:
1.11 kristaps 291: tbl_char(tp, '-', col->width);
1.7 kristaps 292: break;
1.27 schwarze 293: case TBL_CELL_DHORIZ:
1.11 kristaps 294: tbl_char(tp, '=', col->width);
1.1 kristaps 295: break;
1.27 schwarze 296: case TBL_CELL_LONG:
1.1 kristaps 297: /* FALLTHROUGH */
1.27 schwarze 298: case TBL_CELL_CENTRE:
1.1 kristaps 299: /* FALLTHROUGH */
1.27 schwarze 300: case TBL_CELL_LEFT:
1.1 kristaps 301: /* FALLTHROUGH */
1.27 schwarze 302: case TBL_CELL_RIGHT:
1.11 kristaps 303: tbl_literal(tp, dp, col);
1.1 kristaps 304: break;
1.27 schwarze 305: case TBL_CELL_NUMBER:
1.25 schwarze 306: tbl_number(tp, opts, dp, col);
1.18 kristaps 307: break;
1.27 schwarze 308: case TBL_CELL_DOWN:
1.18 kristaps 309: tbl_char(tp, ASCII_NBRSP, col->width);
1.1 kristaps 310: break;
311: default:
312: abort();
313: /* NOTREACHED */
314: }
315: }
1.11 kristaps 316:
1.1 kristaps 317: static void
1.11 kristaps 318: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1 kristaps 319: {
320:
1.22 schwarze 321: tbl_char(tp, ASCII_NBRSP, 1);
322: if (0 < hp->vert)
323: tbl_char(tp, '|', hp->vert);
324: if (2 > hp->vert)
325: tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
1.1 kristaps 326: }
327:
328: static void
1.12 kristaps 329: tbl_char(struct termp *tp, char c, size_t len)
1.1 kristaps 330: {
1.12 kristaps 331: size_t i, sz;
332: char cp[2];
333:
334: cp[0] = c;
335: cp[1] = '\0';
1.1 kristaps 336:
1.3 kristaps 337: sz = term_strlen(tp, cp);
338:
339: for (i = 0; i < len; i += sz)
1.1 kristaps 340: term_word(tp, cp);
341: }
342:
343: static void
1.27 schwarze 344: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
1.11 kristaps 345: const struct roffcol *col)
1.1 kristaps 346: {
1.23 schwarze 347: struct tbl_head *hp;
348: size_t width, len, padl, padr;
349: int spans;
1.1 kristaps 350:
1.17 kristaps 351: assert(dp->string);
1.21 schwarze 352: len = term_strlen(tp, dp->string);
1.23 schwarze 353:
354: hp = dp->layout->head->next;
355: width = col->width;
356: for (spans = dp->spans; spans--; hp = hp->next)
357: width += tp->tbl.cols[hp->ident].width + 3;
358:
359: padr = width > len ? width - len : 0;
1.21 schwarze 360: padl = 0;
1.1 kristaps 361:
1.16 kristaps 362: switch (dp->layout->pos) {
1.27 schwarze 363: case TBL_CELL_LONG:
1.21 schwarze 364: padl = term_len(tp, 1);
365: padr = padr > padl ? padr - padl : 0;
1.1 kristaps 366: break;
1.27 schwarze 367: case TBL_CELL_CENTRE:
1.21 schwarze 368: if (2 > padr)
1.19 schwarze 369: break;
1.21 schwarze 370: padl = padr / 2;
1.19 schwarze 371: padr -= padl;
1.1 kristaps 372: break;
1.27 schwarze 373: case TBL_CELL_RIGHT:
1.21 schwarze 374: padl = padr;
375: padr = 0;
1.1 kristaps 376: break;
377: default:
378: break;
379: }
380:
381: tbl_char(tp, ASCII_NBRSP, padl);
1.29 schwarze 382: tbl_word(tp, dp);
1.21 schwarze 383: tbl_char(tp, ASCII_NBRSP, padr);
1.1 kristaps 384: }
385:
386: static void
1.25 schwarze 387: tbl_number(struct termp *tp, const struct tbl_opts *opts,
1.2 kristaps 388: const struct tbl_dat *dp,
1.11 kristaps 389: const struct roffcol *col)
1.1 kristaps 390: {
1.11 kristaps 391: char *cp;
392: char buf[2];
393: size_t sz, psz, ssz, d, padl;
394: int i;
1.1 kristaps 395:
396: /*
397: * See calc_data_number(). Left-pad by taking the offset of our
398: * and the maximum decimal; right-pad by the remaining amount.
399: */
400:
1.17 kristaps 401: assert(dp->string);
1.2 kristaps 402:
1.17 kristaps 403: sz = term_strlen(tp, dp->string);
1.2 kristaps 404:
1.25 schwarze 405: buf[0] = opts->decimal;
1.11 kristaps 406: buf[1] = '\0';
1.2 kristaps 407:
1.11 kristaps 408: psz = term_strlen(tp, buf);
1.10 kristaps 409:
1.25 schwarze 410: if (NULL != (cp = strrchr(dp->string, opts->decimal))) {
1.5 kristaps 411: buf[1] = '\0';
1.17 kristaps 412: for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
413: buf[0] = dp->string[i];
1.5 kristaps 414: ssz += term_strlen(tp, buf);
415: }
416: d = ssz + psz;
417: } else
418: d = sz + psz;
1.2 kristaps 419:
1.11 kristaps 420: padl = col->decimal - d;
1.2 kristaps 421:
1.11 kristaps 422: tbl_char(tp, ASCII_NBRSP, padl);
1.29 schwarze 423: tbl_word(tp, dp);
1.21 schwarze 424: if (col->width > sz + padl)
425: tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2 kristaps 426: }
427:
1.29 schwarze 428: static void
429: tbl_word(struct termp *tp, const struct tbl_dat *dp)
430: {
431: const void *prev_font;
432:
433: prev_font = term_fontq(tp);
434: if (dp->layout->flags & TBL_CELL_BOLD)
435: term_fontpush(tp, TERMFONT_BOLD);
436: else if (dp->layout->flags & TBL_CELL_ITALIC)
437: term_fontpush(tp, TERMFONT_UNDER);
438:
439: term_word(tp, dp->string);
440:
441: term_fontpopq(tp, prev_font);
442: }
CVSweb