xref: /freebsd/contrib/mandoc/tbl_html.c (revision 6d38604fc532a3fc060788e3ce40464b46047eaf)
1*6d38604fSBaptiste Daroussin /*	$Id: tbl_html.c,v 1.38 2021/09/09 16:52:52 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*6d38604fSBaptiste Daroussin  * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin  */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin 
2061d06d6bSBaptiste Daroussin #include <sys/types.h>
2161d06d6bSBaptiste Daroussin 
2261d06d6bSBaptiste Daroussin #include <assert.h>
2361d06d6bSBaptiste Daroussin #include <stdio.h>
2461d06d6bSBaptiste Daroussin #include <stdlib.h>
2561d06d6bSBaptiste Daroussin #include <string.h>
2661d06d6bSBaptiste Daroussin 
2761d06d6bSBaptiste Daroussin #include "mandoc.h"
2845a5aec3SBaptiste Daroussin #include "roff.h"
297295610fSBaptiste Daroussin #include "tbl.h"
3061d06d6bSBaptiste Daroussin #include "out.h"
3161d06d6bSBaptiste Daroussin #include "html.h"
3261d06d6bSBaptiste Daroussin 
3361d06d6bSBaptiste Daroussin static	void	 html_tblopen(struct html *, const struct tbl_span *);
3461d06d6bSBaptiste Daroussin static	size_t	 html_tbl_len(size_t, void *);
3561d06d6bSBaptiste Daroussin static	size_t	 html_tbl_strlen(const char *, void *);
3661d06d6bSBaptiste Daroussin static	size_t	 html_tbl_sulen(const struct roffsu *, void *);
3761d06d6bSBaptiste Daroussin 
3861d06d6bSBaptiste Daroussin 
3961d06d6bSBaptiste Daroussin static size_t
4061d06d6bSBaptiste Daroussin html_tbl_len(size_t sz, void *arg)
4161d06d6bSBaptiste Daroussin {
4261d06d6bSBaptiste Daroussin 	return sz;
4361d06d6bSBaptiste Daroussin }
4461d06d6bSBaptiste Daroussin 
4561d06d6bSBaptiste Daroussin static size_t
4661d06d6bSBaptiste Daroussin html_tbl_strlen(const char *p, void *arg)
4761d06d6bSBaptiste Daroussin {
4861d06d6bSBaptiste Daroussin 	return strlen(p);
4961d06d6bSBaptiste Daroussin }
5061d06d6bSBaptiste Daroussin 
5161d06d6bSBaptiste Daroussin static size_t
5261d06d6bSBaptiste Daroussin html_tbl_sulen(const struct roffsu *su, void *arg)
5361d06d6bSBaptiste Daroussin {
5461d06d6bSBaptiste Daroussin 	if (su->scale < 0.0)
5561d06d6bSBaptiste Daroussin 		return 0;
5661d06d6bSBaptiste Daroussin 
5761d06d6bSBaptiste Daroussin 	switch (su->unit) {
5861d06d6bSBaptiste Daroussin 	case SCALE_FS:  /* 2^16 basic units */
5961d06d6bSBaptiste Daroussin 		return su->scale * 65536.0 / 24.0;
6061d06d6bSBaptiste Daroussin 	case SCALE_IN:  /* 10 characters per inch */
6161d06d6bSBaptiste Daroussin 		return su->scale * 10.0;
6261d06d6bSBaptiste Daroussin 	case SCALE_CM:  /* 2.54 cm per inch */
6361d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 2.54;
6461d06d6bSBaptiste Daroussin 	case SCALE_PC:  /* 6 pica per inch */
6561d06d6bSBaptiste Daroussin 	case SCALE_VS:
6661d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 6.0;
6761d06d6bSBaptiste Daroussin 	case SCALE_EN:
6861d06d6bSBaptiste Daroussin 	case SCALE_EM:
6961d06d6bSBaptiste Daroussin 		return su->scale;
7061d06d6bSBaptiste Daroussin 	case SCALE_PT:  /* 12 points per pica */
7161d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 6.0 / 12.0;
7261d06d6bSBaptiste Daroussin 	case SCALE_BU:  /* 24 basic units per character */
7361d06d6bSBaptiste Daroussin 		return su->scale / 24.0;
7461d06d6bSBaptiste Daroussin 	case SCALE_MM:  /* 1/1000 inch */
7561d06d6bSBaptiste Daroussin 		return su->scale / 100.0;
7661d06d6bSBaptiste Daroussin 	default:
7761d06d6bSBaptiste Daroussin 		abort();
7861d06d6bSBaptiste Daroussin 	}
7961d06d6bSBaptiste Daroussin }
8061d06d6bSBaptiste Daroussin 
8161d06d6bSBaptiste Daroussin static void
8261d06d6bSBaptiste Daroussin html_tblopen(struct html *h, const struct tbl_span *sp)
8361d06d6bSBaptiste Daroussin {
847295610fSBaptiste Daroussin 	html_close_paragraph(h);
8561d06d6bSBaptiste Daroussin 	if (h->tbl.cols == NULL) {
8661d06d6bSBaptiste Daroussin 		h->tbl.len = html_tbl_len;
8761d06d6bSBaptiste Daroussin 		h->tbl.slen = html_tbl_strlen;
8861d06d6bSBaptiste Daroussin 		h->tbl.sulen = html_tbl_sulen;
8961d06d6bSBaptiste Daroussin 		tblcalc(&h->tbl, sp, 0, 0);
9061d06d6bSBaptiste Daroussin 	}
9161d06d6bSBaptiste Daroussin 	assert(NULL == h->tblt);
927295610fSBaptiste Daroussin 	h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
937295610fSBaptiste Daroussin 	    "border",
947295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
957295610fSBaptiste Daroussin 	    "border-style",
967295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_DBOX ? "double" :
977295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
987295610fSBaptiste Daroussin 	    "border-top-style",
997295610fSBaptiste Daroussin 		sp->pos == TBL_SPAN_DHORIZ ? "double" :
1007295610fSBaptiste Daroussin 		sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
10161d06d6bSBaptiste Daroussin }
10261d06d6bSBaptiste Daroussin 
10361d06d6bSBaptiste Daroussin void
10461d06d6bSBaptiste Daroussin print_tblclose(struct html *h)
10561d06d6bSBaptiste Daroussin {
10661d06d6bSBaptiste Daroussin 
10761d06d6bSBaptiste Daroussin 	assert(h->tblt);
10861d06d6bSBaptiste Daroussin 	print_tagq(h, h->tblt);
10961d06d6bSBaptiste Daroussin 	h->tblt = NULL;
11061d06d6bSBaptiste Daroussin }
11161d06d6bSBaptiste Daroussin 
11261d06d6bSBaptiste Daroussin void
11361d06d6bSBaptiste Daroussin print_tbl(struct html *h, const struct tbl_span *sp)
11461d06d6bSBaptiste Daroussin {
11561d06d6bSBaptiste Daroussin 	const struct tbl_dat	*dp;
1167295610fSBaptiste Daroussin 	const struct tbl_cell	*cp;
1177295610fSBaptiste Daroussin 	const struct tbl_span	*psp;
118*6d38604fSBaptiste Daroussin 	const struct roffcol	*col;
11961d06d6bSBaptiste Daroussin 	struct tag		*tt;
1207295610fSBaptiste Daroussin 	const char		*hspans, *vspans, *halign, *valign;
1217295610fSBaptiste Daroussin 	const char		*bborder, *lborder, *rborder;
122*6d38604fSBaptiste Daroussin 	const char		*ccp;
1237295610fSBaptiste Daroussin 	char			 hbuf[4], vbuf[4];
124*6d38604fSBaptiste Daroussin 	size_t			 sz;
125*6d38604fSBaptiste Daroussin 	enum mandoc_esc		 save_font;
1267295610fSBaptiste Daroussin 	int			 i;
12761d06d6bSBaptiste Daroussin 
12861d06d6bSBaptiste Daroussin 	if (h->tblt == NULL)
12961d06d6bSBaptiste Daroussin 		html_tblopen(h, sp);
13061d06d6bSBaptiste Daroussin 
1317295610fSBaptiste Daroussin 	/*
1327295610fSBaptiste Daroussin 	 * Horizontal lines spanning the whole table
1337295610fSBaptiste Daroussin 	 * are handled by previous or following table rows.
1347295610fSBaptiste Daroussin 	 */
1357295610fSBaptiste Daroussin 
1367295610fSBaptiste Daroussin 	if (sp->pos != TBL_SPAN_DATA)
1377295610fSBaptiste Daroussin 		return;
1387295610fSBaptiste Daroussin 
1397295610fSBaptiste Daroussin 	/* Inhibit printing of spaces: we do padding ourselves. */
14061d06d6bSBaptiste Daroussin 
14161d06d6bSBaptiste Daroussin 	h->flags |= HTML_NONOSPACE;
14261d06d6bSBaptiste Daroussin 	h->flags |= HTML_NOSPACE;
14361d06d6bSBaptiste Daroussin 
1447295610fSBaptiste Daroussin 	/* Draw a vertical line left of this row? */
14561d06d6bSBaptiste Daroussin 
1467295610fSBaptiste Daroussin 	switch (sp->layout->vert) {
1477295610fSBaptiste Daroussin 	case 2:
1487295610fSBaptiste Daroussin 		lborder = "double";
1497295610fSBaptiste Daroussin 		break;
1507295610fSBaptiste Daroussin 	case 1:
1517295610fSBaptiste Daroussin 		lborder = "solid";
15261d06d6bSBaptiste Daroussin 		break;
15361d06d6bSBaptiste Daroussin 	default:
1547295610fSBaptiste Daroussin 		lborder = NULL;
1557295610fSBaptiste Daroussin 		break;
1567295610fSBaptiste Daroussin 	}
15761d06d6bSBaptiste Daroussin 
1587295610fSBaptiste Daroussin 	/* Draw a horizontal line below this row? */
1597295610fSBaptiste Daroussin 
1607295610fSBaptiste Daroussin 	bborder = NULL;
1617295610fSBaptiste Daroussin 	if ((psp = sp->next) != NULL) {
1627295610fSBaptiste Daroussin 		switch (psp->pos) {
1637295610fSBaptiste Daroussin 		case TBL_SPAN_DHORIZ:
1647295610fSBaptiste Daroussin 			bborder = "double";
1657295610fSBaptiste Daroussin 			break;
1667295610fSBaptiste Daroussin 		case TBL_SPAN_HORIZ:
1677295610fSBaptiste Daroussin 			bborder = "solid";
1687295610fSBaptiste Daroussin 			break;
1697295610fSBaptiste Daroussin 		default:
1707295610fSBaptiste Daroussin 			break;
1717295610fSBaptiste Daroussin 		}
1727295610fSBaptiste Daroussin 	}
1737295610fSBaptiste Daroussin 
1747295610fSBaptiste Daroussin 	tt = print_otag(h, TAG_TR, "ss",
1757295610fSBaptiste Daroussin 	    "border-left-style", lborder,
1767295610fSBaptiste Daroussin 	    "border-bottom-style", bborder);
1777295610fSBaptiste Daroussin 
1787295610fSBaptiste Daroussin 	for (dp = sp->first; dp != NULL; dp = dp->next) {
1797295610fSBaptiste Daroussin 		print_stagq(h, tt);
1807295610fSBaptiste Daroussin 
1817295610fSBaptiste Daroussin 		/*
1827295610fSBaptiste Daroussin 		 * Do not generate <td> elements for continuations
1837295610fSBaptiste Daroussin 		 * of spanned cells.  Larger <td> elements covering
1847295610fSBaptiste Daroussin 		 * this space were already generated earlier.
1857295610fSBaptiste Daroussin 		 */
1867295610fSBaptiste Daroussin 
1877295610fSBaptiste Daroussin 		cp = dp->layout;
1887295610fSBaptiste Daroussin 		if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
1897295610fSBaptiste Daroussin 		    (dp->string != NULL && strcmp(dp->string, "\\^") == 0))
19061d06d6bSBaptiste Daroussin 			continue;
1917295610fSBaptiste Daroussin 
1927295610fSBaptiste Daroussin 		/* Determine the attribute values. */
1937295610fSBaptiste Daroussin 
1947295610fSBaptiste Daroussin 		if (dp->hspans > 0) {
1957295610fSBaptiste Daroussin 			(void)snprintf(hbuf, sizeof(hbuf),
1967295610fSBaptiste Daroussin 			    "%d", dp->hspans + 1);
1977295610fSBaptiste Daroussin 			hspans = hbuf;
1987295610fSBaptiste Daroussin 		} else
1997295610fSBaptiste Daroussin 			hspans = NULL;
2007295610fSBaptiste Daroussin 		if (dp->vspans > 0) {
2017295610fSBaptiste Daroussin 			(void)snprintf(vbuf, sizeof(vbuf),
2027295610fSBaptiste Daroussin 			    "%d", dp->vspans + 1);
2037295610fSBaptiste Daroussin 			vspans = vbuf;
2047295610fSBaptiste Daroussin 		} else
2057295610fSBaptiste Daroussin 			vspans = NULL;
2067295610fSBaptiste Daroussin 
2077295610fSBaptiste Daroussin 		switch (cp->pos) {
2087295610fSBaptiste Daroussin 		case TBL_CELL_CENTRE:
2097295610fSBaptiste Daroussin 			halign = "center";
2107295610fSBaptiste Daroussin 			break;
2117295610fSBaptiste Daroussin 		case TBL_CELL_RIGHT:
2127295610fSBaptiste Daroussin 		case TBL_CELL_NUMBER:
2137295610fSBaptiste Daroussin 			halign = "right";
2147295610fSBaptiste Daroussin 			break;
2157295610fSBaptiste Daroussin 		default:
2167295610fSBaptiste Daroussin 			halign = NULL;
2177295610fSBaptiste Daroussin 			break;
2187295610fSBaptiste Daroussin 		}
2197295610fSBaptiste Daroussin 		if (cp->flags & TBL_CELL_TALIGN)
2207295610fSBaptiste Daroussin 			valign = "top";
2217295610fSBaptiste Daroussin 		else if (cp->flags & TBL_CELL_BALIGN)
2227295610fSBaptiste Daroussin 			valign = "bottom";
2237295610fSBaptiste Daroussin 		else
2247295610fSBaptiste Daroussin 			valign = NULL;
2257295610fSBaptiste Daroussin 
2267295610fSBaptiste Daroussin 		for (i = dp->hspans; i > 0; i--)
2277295610fSBaptiste Daroussin 			cp = cp->next;
2287295610fSBaptiste Daroussin 		switch (cp->vert) {
2297295610fSBaptiste Daroussin 		case 2:
2307295610fSBaptiste Daroussin 			rborder = "double";
2317295610fSBaptiste Daroussin 			break;
2327295610fSBaptiste Daroussin 		case 1:
2337295610fSBaptiste Daroussin 			rborder = "solid";
2347295610fSBaptiste Daroussin 			break;
2357295610fSBaptiste Daroussin 		default:
2367295610fSBaptiste Daroussin 			rborder = NULL;
2377295610fSBaptiste Daroussin 			break;
2387295610fSBaptiste Daroussin 		}
2397295610fSBaptiste Daroussin 
2407295610fSBaptiste Daroussin 		/* Print the element and the attributes. */
2417295610fSBaptiste Daroussin 
2427295610fSBaptiste Daroussin 		print_otag(h, TAG_TD, "??sss",
2437295610fSBaptiste Daroussin 		    "colspan", hspans, "rowspan", vspans,
2447295610fSBaptiste Daroussin 		    "vertical-align", valign,
2457295610fSBaptiste Daroussin 		    "text-align", halign,
2467295610fSBaptiste Daroussin 		    "border-right-style", rborder);
247*6d38604fSBaptiste Daroussin 		if (dp->layout->pos == TBL_CELL_HORIZ ||
248*6d38604fSBaptiste Daroussin 		    dp->layout->pos == TBL_CELL_DHORIZ ||
249*6d38604fSBaptiste Daroussin 		    dp->pos == TBL_DATA_HORIZ ||
250*6d38604fSBaptiste Daroussin 		    dp->pos == TBL_DATA_DHORIZ)
251*6d38604fSBaptiste Daroussin 			print_otag(h, TAG_HR, "");
252*6d38604fSBaptiste Daroussin 		else if (dp->string != NULL) {
253*6d38604fSBaptiste Daroussin 			save_font = h->metac;
254*6d38604fSBaptiste Daroussin 			html_setfont(h, dp->layout->font);
255*6d38604fSBaptiste Daroussin 			if (dp->layout->pos == TBL_CELL_LONG)
256*6d38604fSBaptiste Daroussin 				print_text(h, "\\[u2003]");  /* em space */
25761d06d6bSBaptiste Daroussin 			print_text(h, dp->string);
258*6d38604fSBaptiste Daroussin 			if (dp->layout->pos == TBL_CELL_NUMBER) {
259*6d38604fSBaptiste Daroussin 				col = h->tbl.cols + dp->layout->col;
260*6d38604fSBaptiste Daroussin 				if (col->decimal < col->nwidth) {
261*6d38604fSBaptiste Daroussin 					if ((ccp = strrchr(dp->string,
262*6d38604fSBaptiste Daroussin 					    sp->opts->decimal)) == NULL) {
263*6d38604fSBaptiste Daroussin 						/* Punctuation space. */
264*6d38604fSBaptiste Daroussin 						print_text(h, "\\[u2008]");
265*6d38604fSBaptiste Daroussin 						ccp = strchr(dp->string, '\0');
266*6d38604fSBaptiste Daroussin 					} else
267*6d38604fSBaptiste Daroussin 						ccp++;
268*6d38604fSBaptiste Daroussin 					sz = col->nwidth - col->decimal;
269*6d38604fSBaptiste Daroussin 					while (--sz > 0) {
270*6d38604fSBaptiste Daroussin 						if (*ccp == '\0')
271*6d38604fSBaptiste Daroussin 							/* Figure space. */
272*6d38604fSBaptiste Daroussin 							print_text(h,
273*6d38604fSBaptiste Daroussin 							    "\\[u2007]");
274*6d38604fSBaptiste Daroussin 						else
275*6d38604fSBaptiste Daroussin 							ccp++;
276*6d38604fSBaptiste Daroussin 					}
277*6d38604fSBaptiste Daroussin 				}
278*6d38604fSBaptiste Daroussin 			}
279*6d38604fSBaptiste Daroussin 			html_setfont(h, save_font);
280*6d38604fSBaptiste Daroussin 		}
28161d06d6bSBaptiste Daroussin 	}
28261d06d6bSBaptiste Daroussin 
28361d06d6bSBaptiste Daroussin 	print_tagq(h, tt);
28461d06d6bSBaptiste Daroussin 
28561d06d6bSBaptiste Daroussin 	h->flags &= ~HTML_NONOSPACE;
28661d06d6bSBaptiste Daroussin 
28761d06d6bSBaptiste Daroussin 	if (sp->next == NULL) {
28861d06d6bSBaptiste Daroussin 		assert(h->tbl.cols);
28961d06d6bSBaptiste Daroussin 		free(h->tbl.cols);
29061d06d6bSBaptiste Daroussin 		h->tbl.cols = NULL;
29161d06d6bSBaptiste Daroussin 		print_tblclose(h);
29261d06d6bSBaptiste Daroussin 	}
29361d06d6bSBaptiste Daroussin }
294