1*99db7d0eSSascha Wildner /* $Id: tbl_html.c,v 1.38 2021/09/09 16:52:52 schwarze Exp $ */
280387638SSascha Wildner /*
336342e81SSascha Wildner * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*99db7d0eSSascha Wildner * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org>
580387638SSascha Wildner *
680387638SSascha Wildner * Permission to use, copy, modify, and distribute this software for any
780387638SSascha Wildner * purpose with or without fee is hereby granted, provided that the above
880387638SSascha Wildner * copyright notice and this permission notice appear in all copies.
980387638SSascha Wildner *
1080387638SSascha Wildner * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1180387638SSascha Wildner * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1280387638SSascha Wildner * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1380387638SSascha Wildner * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1480387638SSascha Wildner * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1580387638SSascha Wildner * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1680387638SSascha Wildner * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1780387638SSascha Wildner */
1880387638SSascha Wildner #include "config.h"
1954ba9607SSascha Wildner
2054ba9607SSascha Wildner #include <sys/types.h>
2180387638SSascha Wildner
2280387638SSascha Wildner #include <assert.h>
2380387638SSascha Wildner #include <stdio.h>
2480387638SSascha Wildner #include <stdlib.h>
2580387638SSascha Wildner #include <string.h>
2680387638SSascha Wildner
2780387638SSascha Wildner #include "mandoc.h"
28*99db7d0eSSascha Wildner #include "roff.h"
2954ba9607SSascha Wildner #include "tbl.h"
3080387638SSascha Wildner #include "out.h"
3180387638SSascha Wildner #include "html.h"
3280387638SSascha Wildner
3360e1e752SSascha Wildner static void html_tblopen(struct html *, const struct tbl_span *);
3480387638SSascha Wildner static size_t html_tbl_len(size_t, void *);
3580387638SSascha Wildner static size_t html_tbl_strlen(const char *, void *);
3654ba9607SSascha Wildner static size_t html_tbl_sulen(const struct roffsu *, void *);
3780387638SSascha Wildner
38070c62a6SFranco Fichtner
3980387638SSascha Wildner static size_t
html_tbl_len(size_t sz,void * arg)4080387638SSascha Wildner html_tbl_len(size_t sz, void *arg)
4180387638SSascha Wildner {
4254ba9607SSascha Wildner return sz;
4380387638SSascha Wildner }
4480387638SSascha Wildner
4580387638SSascha Wildner static size_t
html_tbl_strlen(const char * p,void * arg)4680387638SSascha Wildner html_tbl_strlen(const char *p, void *arg)
4780387638SSascha Wildner {
4854ba9607SSascha Wildner return strlen(p);
4954ba9607SSascha Wildner }
5080387638SSascha Wildner
5154ba9607SSascha Wildner static size_t
html_tbl_sulen(const struct roffsu * su,void * arg)5254ba9607SSascha Wildner html_tbl_sulen(const struct roffsu *su, void *arg)
5354ba9607SSascha Wildner {
5454ba9607SSascha Wildner if (su->scale < 0.0)
5554ba9607SSascha Wildner return 0;
5654ba9607SSascha Wildner
5754ba9607SSascha Wildner switch (su->unit) {
5854ba9607SSascha Wildner case SCALE_FS: /* 2^16 basic units */
5954ba9607SSascha Wildner return su->scale * 65536.0 / 24.0;
6054ba9607SSascha Wildner case SCALE_IN: /* 10 characters per inch */
6154ba9607SSascha Wildner return su->scale * 10.0;
6254ba9607SSascha Wildner case SCALE_CM: /* 2.54 cm per inch */
6354ba9607SSascha Wildner return su->scale * 10.0 / 2.54;
6454ba9607SSascha Wildner case SCALE_PC: /* 6 pica per inch */
6554ba9607SSascha Wildner case SCALE_VS:
6654ba9607SSascha Wildner return su->scale * 10.0 / 6.0;
6754ba9607SSascha Wildner case SCALE_EN:
6854ba9607SSascha Wildner case SCALE_EM:
6954ba9607SSascha Wildner return su->scale;
7054ba9607SSascha Wildner case SCALE_PT: /* 12 points per pica */
7154ba9607SSascha Wildner return su->scale * 10.0 / 6.0 / 12.0;
7254ba9607SSascha Wildner case SCALE_BU: /* 24 basic units per character */
7354ba9607SSascha Wildner return su->scale / 24.0;
7454ba9607SSascha Wildner case SCALE_MM: /* 1/1000 inch */
7554ba9607SSascha Wildner return su->scale / 100.0;
7654ba9607SSascha Wildner default:
7754ba9607SSascha Wildner abort();
7854ba9607SSascha Wildner }
7980387638SSascha Wildner }
8080387638SSascha Wildner
8160e1e752SSascha Wildner static void
html_tblopen(struct html * h,const struct tbl_span * sp)8260e1e752SSascha Wildner html_tblopen(struct html *h, const struct tbl_span *sp)
8380387638SSascha Wildner {
8454ba9607SSascha Wildner html_close_paragraph(h);
8554ba9607SSascha Wildner if (h->tbl.cols == NULL) {
8680387638SSascha Wildner h->tbl.len = html_tbl_len;
8780387638SSascha Wildner h->tbl.slen = html_tbl_strlen;
8854ba9607SSascha Wildner h->tbl.sulen = html_tbl_sulen;
8954ba9607SSascha Wildner tblcalc(&h->tbl, sp, 0, 0);
9080387638SSascha Wildner }
9160e1e752SSascha Wildner assert(NULL == h->tblt);
9254ba9607SSascha Wildner h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
9354ba9607SSascha Wildner "border",
9454ba9607SSascha Wildner sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
9554ba9607SSascha Wildner "border-style",
9654ba9607SSascha Wildner sp->opts->opts & TBL_OPT_DBOX ? "double" :
9754ba9607SSascha Wildner sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
9854ba9607SSascha Wildner "border-top-style",
9954ba9607SSascha Wildner sp->pos == TBL_SPAN_DHORIZ ? "double" :
10054ba9607SSascha Wildner sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
10160e1e752SSascha Wildner }
10260e1e752SSascha Wildner
10360e1e752SSascha Wildner void
print_tblclose(struct html * h)10460e1e752SSascha Wildner print_tblclose(struct html *h)
10560e1e752SSascha Wildner {
10660e1e752SSascha Wildner
10760e1e752SSascha Wildner assert(h->tblt);
10860e1e752SSascha Wildner print_tagq(h, h->tblt);
10960e1e752SSascha Wildner h->tblt = NULL;
11060e1e752SSascha Wildner }
11160e1e752SSascha Wildner
11260e1e752SSascha Wildner void
print_tbl(struct html * h,const struct tbl_span * sp)11360e1e752SSascha Wildner print_tbl(struct html *h, const struct tbl_span *sp)
11460e1e752SSascha Wildner {
11560e1e752SSascha Wildner const struct tbl_dat *dp;
11654ba9607SSascha Wildner const struct tbl_cell *cp;
11754ba9607SSascha Wildner const struct tbl_span *psp;
118*99db7d0eSSascha Wildner const struct roffcol *col;
11960e1e752SSascha Wildner struct tag *tt;
12054ba9607SSascha Wildner const char *hspans, *vspans, *halign, *valign;
12154ba9607SSascha Wildner const char *bborder, *lborder, *rborder;
122*99db7d0eSSascha Wildner const char *ccp;
12354ba9607SSascha Wildner char hbuf[4], vbuf[4];
124*99db7d0eSSascha Wildner size_t sz;
125*99db7d0eSSascha Wildner enum mandoc_esc save_font;
12654ba9607SSascha Wildner int i;
12760e1e752SSascha Wildner
12854ba9607SSascha Wildner if (h->tblt == NULL)
12960e1e752SSascha Wildner html_tblopen(h, sp);
13060e1e752SSascha Wildner
13154ba9607SSascha Wildner /*
13254ba9607SSascha Wildner * Horizontal lines spanning the whole table
13354ba9607SSascha Wildner * are handled by previous or following table rows.
13454ba9607SSascha Wildner */
13554ba9607SSascha Wildner
13654ba9607SSascha Wildner if (sp->pos != TBL_SPAN_DATA)
13754ba9607SSascha Wildner return;
13854ba9607SSascha Wildner
13954ba9607SSascha Wildner /* Inhibit printing of spaces: we do padding ourselves. */
14060e1e752SSascha Wildner
14160e1e752SSascha Wildner h->flags |= HTML_NONOSPACE;
14260e1e752SSascha Wildner h->flags |= HTML_NOSPACE;
14360e1e752SSascha Wildner
14454ba9607SSascha Wildner /* Draw a vertical line left of this row? */
14560e1e752SSascha Wildner
14654ba9607SSascha Wildner switch (sp->layout->vert) {
14754ba9607SSascha Wildner case 2:
14854ba9607SSascha Wildner lborder = "double";
14954ba9607SSascha Wildner break;
15054ba9607SSascha Wildner case 1:
15154ba9607SSascha Wildner lborder = "solid";
15280387638SSascha Wildner break;
15380387638SSascha Wildner default:
15454ba9607SSascha Wildner lborder = NULL;
15560e1e752SSascha Wildner break;
15660e1e752SSascha Wildner }
15754ba9607SSascha Wildner
15854ba9607SSascha Wildner /* Draw a horizontal line below this row? */
15954ba9607SSascha Wildner
16054ba9607SSascha Wildner bborder = NULL;
16154ba9607SSascha Wildner if ((psp = sp->next) != NULL) {
16254ba9607SSascha Wildner switch (psp->pos) {
16354ba9607SSascha Wildner case TBL_SPAN_DHORIZ:
16454ba9607SSascha Wildner bborder = "double";
16560e1e752SSascha Wildner break;
16654ba9607SSascha Wildner case TBL_SPAN_HORIZ:
16754ba9607SSascha Wildner bborder = "solid";
16854ba9607SSascha Wildner break;
16954ba9607SSascha Wildner default:
17054ba9607SSascha Wildner break;
17154ba9607SSascha Wildner }
17254ba9607SSascha Wildner }
17354ba9607SSascha Wildner
17454ba9607SSascha Wildner tt = print_otag(h, TAG_TR, "ss",
17554ba9607SSascha Wildner "border-left-style", lborder,
17654ba9607SSascha Wildner "border-bottom-style", bborder);
17754ba9607SSascha Wildner
17854ba9607SSascha Wildner for (dp = sp->first; dp != NULL; dp = dp->next) {
17954ba9607SSascha Wildner print_stagq(h, tt);
18054ba9607SSascha Wildner
18154ba9607SSascha Wildner /*
18254ba9607SSascha Wildner * Do not generate <td> elements for continuations
18354ba9607SSascha Wildner * of spanned cells. Larger <td> elements covering
18454ba9607SSascha Wildner * this space were already generated earlier.
18554ba9607SSascha Wildner */
18654ba9607SSascha Wildner
18754ba9607SSascha Wildner cp = dp->layout;
18854ba9607SSascha Wildner if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
18954ba9607SSascha Wildner (dp->string != NULL && strcmp(dp->string, "\\^") == 0))
19054ba9607SSascha Wildner continue;
19154ba9607SSascha Wildner
19254ba9607SSascha Wildner /* Determine the attribute values. */
19354ba9607SSascha Wildner
19454ba9607SSascha Wildner if (dp->hspans > 0) {
19554ba9607SSascha Wildner (void)snprintf(hbuf, sizeof(hbuf),
19654ba9607SSascha Wildner "%d", dp->hspans + 1);
19754ba9607SSascha Wildner hspans = hbuf;
19854ba9607SSascha Wildner } else
19954ba9607SSascha Wildner hspans = NULL;
20054ba9607SSascha Wildner if (dp->vspans > 0) {
20154ba9607SSascha Wildner (void)snprintf(vbuf, sizeof(vbuf),
20254ba9607SSascha Wildner "%d", dp->vspans + 1);
20354ba9607SSascha Wildner vspans = vbuf;
20454ba9607SSascha Wildner } else
20554ba9607SSascha Wildner vspans = NULL;
20654ba9607SSascha Wildner
20754ba9607SSascha Wildner switch (cp->pos) {
20854ba9607SSascha Wildner case TBL_CELL_CENTRE:
20954ba9607SSascha Wildner halign = "center";
21054ba9607SSascha Wildner break;
21154ba9607SSascha Wildner case TBL_CELL_RIGHT:
21254ba9607SSascha Wildner case TBL_CELL_NUMBER:
21354ba9607SSascha Wildner halign = "right";
21454ba9607SSascha Wildner break;
21554ba9607SSascha Wildner default:
21654ba9607SSascha Wildner halign = NULL;
21754ba9607SSascha Wildner break;
21854ba9607SSascha Wildner }
21954ba9607SSascha Wildner if (cp->flags & TBL_CELL_TALIGN)
22054ba9607SSascha Wildner valign = "top";
22154ba9607SSascha Wildner else if (cp->flags & TBL_CELL_BALIGN)
22254ba9607SSascha Wildner valign = "bottom";
22354ba9607SSascha Wildner else
22454ba9607SSascha Wildner valign = NULL;
22554ba9607SSascha Wildner
22654ba9607SSascha Wildner for (i = dp->hspans; i > 0; i--)
22754ba9607SSascha Wildner cp = cp->next;
22854ba9607SSascha Wildner switch (cp->vert) {
22954ba9607SSascha Wildner case 2:
23054ba9607SSascha Wildner rborder = "double";
23154ba9607SSascha Wildner break;
23254ba9607SSascha Wildner case 1:
23354ba9607SSascha Wildner rborder = "solid";
23454ba9607SSascha Wildner break;
23554ba9607SSascha Wildner default:
23654ba9607SSascha Wildner rborder = NULL;
23754ba9607SSascha Wildner break;
23854ba9607SSascha Wildner }
23954ba9607SSascha Wildner
24054ba9607SSascha Wildner /* Print the element and the attributes. */
24154ba9607SSascha Wildner
24254ba9607SSascha Wildner print_otag(h, TAG_TD, "??sss",
24354ba9607SSascha Wildner "colspan", hspans, "rowspan", vspans,
24454ba9607SSascha Wildner "vertical-align", valign,
24554ba9607SSascha Wildner "text-align", halign,
24654ba9607SSascha Wildner "border-right-style", rborder);
247*99db7d0eSSascha Wildner if (dp->layout->pos == TBL_CELL_HORIZ ||
248*99db7d0eSSascha Wildner dp->layout->pos == TBL_CELL_DHORIZ ||
249*99db7d0eSSascha Wildner dp->pos == TBL_DATA_HORIZ ||
250*99db7d0eSSascha Wildner dp->pos == TBL_DATA_DHORIZ)
251*99db7d0eSSascha Wildner print_otag(h, TAG_HR, "");
252*99db7d0eSSascha Wildner else if (dp->string != NULL) {
253*99db7d0eSSascha Wildner save_font = h->metac;
254*99db7d0eSSascha Wildner html_setfont(h, dp->layout->font);
255*99db7d0eSSascha Wildner if (dp->layout->pos == TBL_CELL_LONG)
256*99db7d0eSSascha Wildner print_text(h, "\\[u2003]"); /* em space */
25754ba9607SSascha Wildner print_text(h, dp->string);
258*99db7d0eSSascha Wildner if (dp->layout->pos == TBL_CELL_NUMBER) {
259*99db7d0eSSascha Wildner col = h->tbl.cols + dp->layout->col;
260*99db7d0eSSascha Wildner if (col->decimal < col->nwidth) {
261*99db7d0eSSascha Wildner if ((ccp = strrchr(dp->string,
262*99db7d0eSSascha Wildner sp->opts->decimal)) == NULL) {
263*99db7d0eSSascha Wildner /* Punctuation space. */
264*99db7d0eSSascha Wildner print_text(h, "\\[u2008]");
265*99db7d0eSSascha Wildner ccp = strchr(dp->string, '\0');
266*99db7d0eSSascha Wildner } else
267*99db7d0eSSascha Wildner ccp++;
268*99db7d0eSSascha Wildner sz = col->nwidth - col->decimal;
269*99db7d0eSSascha Wildner while (--sz > 0) {
270*99db7d0eSSascha Wildner if (*ccp == '\0')
271*99db7d0eSSascha Wildner /* Figure space. */
272*99db7d0eSSascha Wildner print_text(h,
273*99db7d0eSSascha Wildner "\\[u2007]");
274*99db7d0eSSascha Wildner else
275*99db7d0eSSascha Wildner ccp++;
276*99db7d0eSSascha Wildner }
277*99db7d0eSSascha Wildner }
278*99db7d0eSSascha Wildner }
279*99db7d0eSSascha Wildner html_setfont(h, save_font);
280*99db7d0eSSascha Wildner }
28160e1e752SSascha Wildner }
28280387638SSascha Wildner
28380387638SSascha Wildner print_tagq(h, tt);
28480387638SSascha Wildner
28580387638SSascha Wildner h->flags &= ~HTML_NONOSPACE;
28680387638SSascha Wildner
28754ba9607SSascha Wildner if (sp->next == NULL) {
28880387638SSascha Wildner assert(h->tbl.cols);
28980387638SSascha Wildner free(h->tbl.cols);
29080387638SSascha Wildner h->tbl.cols = NULL;
29160e1e752SSascha Wildner print_tblclose(h);
29280387638SSascha Wildner }
29380387638SSascha Wildner }
294