]>
git.cameronkatri.com Git - mandoc.git/blob - tbl_term.c
1 /* $Id: tbl_term.c,v 1.60 2018/08/19 23:10:28 schwarze Exp $ */
3 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
20 #include <sys/types.h>
32 #define IS_HORIZ(cp) ((cp)->pos == TBL_CELL_HORIZ || \
33 (cp)->pos == TBL_CELL_DHORIZ)
35 static size_t term_tbl_len(size_t, void *);
36 static size_t term_tbl_strlen(const char *, void *);
37 static size_t term_tbl_sulen(const struct roffsu
*, void *);
38 static void tbl_char(struct termp
*, char, size_t);
39 static void tbl_data(struct termp
*, const struct tbl_opts
*,
40 const struct tbl_cell
*,
41 const struct tbl_dat
*,
42 const struct roffcol
*);
43 static void tbl_literal(struct termp
*, const struct tbl_dat
*,
44 const struct roffcol
*);
45 static void tbl_number(struct termp
*, const struct tbl_opts
*,
46 const struct tbl_dat
*,
47 const struct roffcol
*);
48 static void tbl_hrule(struct termp
*, const struct tbl_span
*, int);
49 static void tbl_word(struct termp
*, const struct tbl_dat
*);
53 term_tbl_sulen(const struct roffsu
*su
, void *arg
)
57 i
= term_hen((const struct termp
*)arg
, su
);
62 term_tbl_strlen(const char *p
, void *arg
)
64 return term_strlen((const struct termp
*)arg
, p
);
68 term_tbl_len(size_t sz
, void *arg
)
70 return term_len((const struct termp
*)arg
, sz
);
74 term_tbl(struct termp
*tp
, const struct tbl_span
*sp
)
76 const struct tbl_cell
*cp
, *cpn
, *cpp
;
77 const struct tbl_dat
*dp
;
80 int ic
, horiz
, spans
, vert
, more
;
83 /* Inhibit printing of spaces: we do padding ourselves. */
85 tp
->flags
|= TERMP_NOSPACE
| TERMP_NONOSPACE
;
88 * The first time we're invoked for a given table block,
89 * calculate the table widths and decimal positions.
92 if (tp
->tbl
.cols
== NULL
) {
93 tp
->tbl
.len
= term_tbl_len
;
94 tp
->tbl
.slen
= term_tbl_strlen
;
95 tp
->tbl
.sulen
= term_tbl_sulen
;
98 tblcalc(&tp
->tbl
, sp
, tp
->tcol
->offset
, tp
->tcol
->rmargin
);
100 /* Tables leak .ta settings to subsequent text. */
102 term_tab_set(tp
, NULL
);
103 coloff
= sp
->opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
) ||
105 for (ic
= 0; ic
< sp
->opts
->cols
; ic
++) {
106 coloff
+= tp
->tbl
.cols
[ic
].width
;
107 term_tab_iset(coloff
);
108 coloff
+= tp
->tbl
.cols
[ic
].spacing
;
111 /* Center the table as a whole. */
113 offset
= tp
->tcol
->offset
;
114 if (sp
->opts
->opts
& TBL_OPT_CENTRE
) {
115 tsz
= sp
->opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
)
116 ? 2 : !!sp
->opts
->lvert
+ !!sp
->opts
->rvert
;
117 for (ic
= 0; ic
+ 1 < sp
->opts
->cols
; ic
++)
118 tsz
+= tp
->tbl
.cols
[ic
].width
+
119 tp
->tbl
.cols
[ic
].spacing
;
121 tsz
+= tp
->tbl
.cols
[sp
->opts
->cols
- 1].width
;
122 if (offset
+ tsz
> tp
->tcol
->rmargin
)
124 tp
->tcol
->offset
= offset
+ tp
->tcol
->rmargin
> tsz
?
125 (offset
+ tp
->tcol
->rmargin
- tsz
) / 2 : 0;
128 /* Horizontal frame at the start of boxed tables. */
130 if (sp
->opts
->opts
& TBL_OPT_DBOX
)
131 tbl_hrule(tp
, sp
, 3);
132 if (sp
->opts
->opts
& (TBL_OPT_DBOX
| TBL_OPT_BOX
))
133 tbl_hrule(tp
, sp
, 2);
136 /* Set up the columns. */
138 tp
->flags
|= TERMP_MULTICOL
;
142 case TBL_SPAN_DHORIZ
:
147 term_setcol(tp
, sp
->opts
->cols
+ 2);
148 coloff
= tp
->tcol
->offset
;
150 /* Set up a column for a left vertical frame. */
152 if (sp
->opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
) ||
155 tp
->tcol
->rmargin
= coloff
;
157 /* Set up the data columns. */
161 for (ic
= 0; ic
< sp
->opts
->cols
; ic
++) {
164 tp
->tcol
->offset
= coloff
;
166 coloff
+= tp
->tbl
.cols
[ic
].width
;
167 tp
->tcol
->rmargin
= coloff
;
168 if (ic
+ 1 < sp
->opts
->cols
)
169 coloff
+= tp
->tbl
.cols
[ic
].spacing
;
177 if (ic
|| sp
->layout
->first
->pos
!= TBL_CELL_SPAN
)
181 /* Set up a column for a right vertical frame. */
184 tp
->tcol
->offset
= coloff
+ 1;
185 tp
->tcol
->rmargin
= tp
->maxrmargin
;
187 /* Spans may have reduced the number of columns. */
189 tp
->lasttcol
= tp
->tcol
- tp
->tcols
;
191 /* Fill the buffers for all data columns. */
193 tp
->tcol
= tp
->tcols
;
194 cp
= cpn
= sp
->layout
->first
;
197 for (ic
= 0; ic
< sp
->opts
->cols
; ic
++) {
208 tbl_data(tp
, sp
->opts
, cp
, dp
, tp
->tbl
.cols
+ ic
);
212 if (cp
->pos
!= TBL_CELL_SPAN
)
219 /* Print the vertical frame at the start of each row. */
221 tp
->tcol
= tp
->tcols
;
223 if (sp
->layout
->vert
||
224 (sp
->next
!= NULL
&& sp
->next
->layout
->vert
&&
225 sp
->next
->pos
== TBL_SPAN_DATA
) ||
226 (sp
->prev
!= NULL
&& sp
->prev
->layout
->vert
&&
227 (horiz
|| (IS_HORIZ(sp
->layout
->first
) &&
228 !IS_HORIZ(sp
->prev
->layout
->first
)))) ||
229 sp
->opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
))
230 fc
= horiz
|| IS_HORIZ(sp
->layout
->first
) ? '+' : '|';
231 else if (horiz
&& sp
->opts
->lvert
)
234 (*tp
->advance
)(tp
, tp
->tcols
->offset
);
235 (*tp
->letter
)(tp
, fc
);
236 tp
->viscol
= tp
->tcol
->offset
+ 1;
239 /* Print the data cells. */
243 tbl_hrule(tp
, sp
, 0);
246 cp
= sp
->layout
->first
;
247 cpn
= sp
->next
== NULL
? NULL
:
248 sp
->next
->layout
->first
;
249 cpp
= sp
->prev
== NULL
? NULL
:
250 sp
->prev
->layout
->first
;
253 for (ic
= 0; ic
< sp
->opts
->cols
; ic
++) {
256 * Figure out whether to print a
257 * vertical line after this cell
258 * and advance to next layout cell.
267 case TBL_CELL_DHORIZ
:
285 IS_HORIZ(cp
->next
) &&
286 !IS_HORIZ(cpp
->next
))))
291 sp
->opts
->opts
& TBL_OPT_ALLBOX
)
302 * Skip later cells in a span,
303 * figure out whether to start a span,
304 * and advance to next data cell.
313 if (ic
|| sp
->layout
->first
->pos
319 * Print one line of text in the cell
320 * and remember whether there is more.
324 if (tp
->tcol
->col
< tp
->tcol
->lastcol
)
326 if (tp
->tcol
->col
< tp
->tcol
->lastcol
)
330 * Vertical frames between data cells,
331 * but not after the last column.
334 if (fc
== ' ' && ((vert
== 0 &&
335 (cp
== NULL
|| !IS_HORIZ(cp
))) ||
336 tp
->tcol
+ 1 == tp
->tcols
+ tp
->lasttcol
))
339 if (tp
->viscol
< tp
->tcol
->rmargin
) {
340 (*tp
->advance
)(tp
, tp
->tcol
->rmargin
342 tp
->viscol
= tp
->tcol
->rmargin
;
344 while (tp
->viscol
< tp
->tcol
->rmargin
+
345 tp
->tbl
.cols
[ic
].spacing
/ 2) {
346 (*tp
->letter
)(tp
, fc
);
350 if (tp
->tcol
+ 1 == tp
->tcols
+ tp
->lasttcol
)
353 if (fc
== ' ' && cp
!= NULL
) {
358 case TBL_CELL_DHORIZ
:
365 if (tp
->tbl
.cols
[ic
].spacing
) {
366 (*tp
->letter
)(tp
, fc
== ' ' ? '|' :
373 cp
->pos
== TBL_CELL_HORIZ
)
375 else if (cp
!= NULL
&&
376 cp
->pos
== TBL_CELL_DHORIZ
)
381 if (tp
->tbl
.cols
[ic
].spacing
> 2 &&
382 (vert
> 1 || fc
!= ' ')) {
383 (*tp
->letter
)(tp
, fc
== ' ' ? '|' :
384 vert
> 1 ? '+' : fc
);
390 /* Print the vertical frame at the end of each row. */
393 if ((sp
->layout
->last
->vert
&&
394 sp
->layout
->last
->col
+ 1 == sp
->opts
->cols
) ||
396 sp
->next
->layout
->last
->vert
&&
397 sp
->next
->layout
->last
->col
+ 1 == sp
->opts
->cols
) ||
399 sp
->prev
->layout
->last
->vert
&&
400 sp
->prev
->layout
->last
->col
+ 1 == sp
->opts
->cols
&&
401 (horiz
|| (IS_HORIZ(sp
->layout
->last
) &&
402 !IS_HORIZ(sp
->prev
->layout
->last
)))) ||
403 (sp
->opts
->opts
& (TBL_OPT_BOX
| TBL_OPT_DBOX
)))
404 fc
= horiz
|| IS_HORIZ(sp
->layout
->last
) ? '+' : '|';
405 else if (horiz
&& sp
->opts
->rvert
)
408 if (horiz
== 0 && (IS_HORIZ(sp
->layout
->last
) == 0 ||
409 sp
->layout
->last
->col
+ 1 < sp
->opts
->cols
)) {
412 tp
->tcol
->offset
> tp
->viscol
?
413 tp
->tcol
->offset
- tp
->viscol
: 1);
415 (*tp
->letter
)(tp
, fc
);
422 * Clean up after this row. If it is the last line
423 * of the table, print the box line and clean up
424 * column data; otherwise, print the allbox line.
428 tp
->flags
&= ~TERMP_MULTICOL
;
429 tp
->tcol
->rmargin
= tp
->maxrmargin
;
430 if (sp
->next
== NULL
) {
431 if (sp
->opts
->opts
& (TBL_OPT_DBOX
| TBL_OPT_BOX
)) {
432 tbl_hrule(tp
, sp
, 2);
435 if (sp
->opts
->opts
& TBL_OPT_DBOX
) {
436 tbl_hrule(tp
, sp
, 3);
439 assert(tp
->tbl
.cols
);
442 tp
->tcol
->offset
= offset
;
443 } else if (horiz
== 0 && sp
->opts
->opts
& TBL_OPT_ALLBOX
&&
444 (sp
->next
== NULL
|| sp
->next
->pos
== TBL_SPAN_DATA
||
445 sp
->next
->next
!= NULL
))
446 tbl_hrule(tp
, sp
, 1);
448 tp
->flags
&= ~TERMP_NONOSPACE
;
452 * Kinds of horizontal rulers:
453 * 0: inside the table (single or double line with crossings)
454 * 1: inside the table (single or double line with crossings and ends)
455 * 2: inner frame (single line with crossings and ends)
456 * 3: outer frame (single line without crossings with ends)
459 tbl_hrule(struct termp
*tp
, const struct tbl_span
*sp
, int kind
)
461 const struct tbl_cell
*cp
, *cpn
, *cpp
;
462 const struct roffcol
*col
;
464 char cross
, line
, stdcross
, stdline
;
466 stdline
= (kind
< 2 && TBL_SPAN_DHORIZ
== sp
->pos
) ? '=' : '-';
467 stdcross
= (kind
< 3) ? '+' : '-';
469 cp
= sp
->layout
->first
;
470 cpp
= kind
|| sp
->prev
== NULL
? NULL
: sp
->prev
->layout
->first
;
473 cpn
= kind
> 1 || sp
->next
== NULL
? NULL
: sp
->next
->layout
->first
;
478 cpn
== NULL
|| cpn
->pos
!= TBL_CELL_DOWN
? "+" : "|");
480 col
= tp
->tbl
.cols
+ cp
->col
;
481 if (cpn
== NULL
|| cpn
->pos
!= TBL_CELL_DOWN
) {
486 cross
= (kind
< 3) ? '|' : ' ';
488 tbl_char(tp
, line
, col
->width
+ col
->spacing
/ 2);
490 if ((cp
= cp
->next
) == NULL
)
493 if (vert
< cpp
->vert
)
498 if (vert
< cpn
->vert
)
502 if (cpn
== NULL
|| cpn
->pos
!= TBL_CELL_DOWN
) {
507 if (sp
->opts
->opts
& TBL_OPT_ALLBOX
&& !vert
)
510 tbl_char(tp
, vert
? cross
: line
, 1);
511 if (col
->spacing
> 2)
512 tbl_char(tp
, vert
> 1 ? cross
: line
, 1);
513 if (col
->spacing
> 4)
514 tbl_char(tp
, line
, (col
->spacing
- 3) / 2);
518 cpn
== NULL
|| cpn
->pos
!= TBL_CELL_DOWN
? "+" : "|");
524 tbl_data(struct termp
*tp
, const struct tbl_opts
*opts
,
525 const struct tbl_cell
*cp
, const struct tbl_dat
*dp
,
526 const struct roffcol
*col
)
530 tbl_char(tp
, '-', col
->width
);
532 case TBL_CELL_DHORIZ
:
533 tbl_char(tp
, '=', col
->width
);
546 case TBL_DATA_NHORIZ
:
547 tbl_char(tp
, '-', col
->width
);
549 case TBL_DATA_NDHORIZ
:
550 case TBL_DATA_DHORIZ
:
551 tbl_char(tp
, '=', col
->width
);
559 case TBL_CELL_CENTRE
:
562 tbl_literal(tp
, dp
, col
);
564 case TBL_CELL_NUMBER
:
565 tbl_number(tp
, opts
, dp
, col
);
576 tbl_char(struct termp
*tp
, char c
, size_t len
)
584 sz
= term_strlen(tp
, cp
);
586 for (i
= 0; i
< len
; i
+= sz
)
591 tbl_literal(struct termp
*tp
, const struct tbl_dat
*dp
,
592 const struct roffcol
*col
)
594 size_t len
, padl
, padr
, width
;
598 len
= term_strlen(tp
, dp
->string
);
600 ic
= dp
->layout
->col
;
603 width
+= tp
->tbl
.cols
[++ic
].width
+ 3;
605 padr
= width
> len
? width
- len
: 0;
608 switch (dp
->layout
->pos
) {
610 padl
= term_len(tp
, 1);
611 padr
= padr
> padl
? padr
- padl
: 0;
613 case TBL_CELL_CENTRE
:
627 tbl_char(tp
, ASCII_NBRSP
, padl
);
629 tbl_char(tp
, ASCII_NBRSP
, padr
);
633 tbl_number(struct termp
*tp
, const struct tbl_opts
*opts
,
634 const struct tbl_dat
*dp
,
635 const struct roffcol
*col
)
637 const char *cp
, *lastdigit
, *lastpoint
;
638 size_t intsz
, padl
, totsz
;
642 * Almost the same code as in tblcalc_number():
643 * First find the position of the decimal point.
647 lastdigit
= lastpoint
= NULL
;
648 for (cp
= dp
->string
; cp
[0] != '\0'; cp
++) {
649 if (cp
[0] == '\\' && cp
[1] == '&') {
650 lastdigit
= lastpoint
= cp
;
652 } else if (cp
[0] == opts
->decimal
&&
653 (isdigit((unsigned char)cp
[1]) ||
654 (cp
> dp
->string
&& isdigit((unsigned char)cp
[-1]))))
656 else if (isdigit((unsigned char)cp
[0]))
660 /* Then measure both widths. */
663 totsz
= term_strlen(tp
, dp
->string
);
664 if (lastdigit
!= NULL
) {
665 if (lastpoint
== NULL
)
666 lastpoint
= lastdigit
+ 1;
669 for (cp
= dp
->string
; cp
< lastpoint
; cp
++) {
671 intsz
+= term_strlen(tp
, buf
);
675 * Pad left to match the decimal position,
676 * but avoid exceeding the total column width.
679 if (col
->decimal
> intsz
&& col
->width
> totsz
) {
680 padl
= col
->decimal
- intsz
;
681 if (padl
+ totsz
> col
->width
)
682 padl
= col
->width
- totsz
;
685 /* If it is not a number, simply center the string. */
687 } else if (col
->width
> totsz
)
688 padl
= (col
->width
- totsz
) / 2;
690 tbl_char(tp
, ASCII_NBRSP
, padl
);
693 /* Pad right to fill the column. */
695 if (col
->width
> padl
+ totsz
)
696 tbl_char(tp
, ASCII_NBRSP
, col
->width
- padl
- totsz
);
700 tbl_word(struct termp
*tp
, const struct tbl_dat
*dp
)
704 prev_font
= tp
->fonti
;
705 if (dp
->layout
->flags
& TBL_CELL_BOLD
)
706 term_fontpush(tp
, TERMFONT_BOLD
);
707 else if (dp
->layout
->flags
& TBL_CELL_ITALIC
)
708 term_fontpush(tp
, TERMFONT_UNDER
);
710 term_word(tp
, dp
->string
);
712 term_fontpopq(tp
, prev_font
);