xref: /freebsd/contrib/mandoc/tbl_html.c (revision c1c95add8c80843ba15d784f95c361d795b1f593)
1*c1c95addSBrooks Davis /* $Id: tbl_html.c,v 1.41 2022/04/23 14:02:17 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis  * Copyright (c) 2014, 2015, 2017, 2018, 2021, 2022
4*c1c95addSBrooks Davis  *               Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
661d06d6bSBaptiste Daroussin  *
761d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin  *
1161d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1261d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1461d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1861d06d6bSBaptiste Daroussin  */
1961d06d6bSBaptiste Daroussin #include "config.h"
2061d06d6bSBaptiste Daroussin 
2161d06d6bSBaptiste Daroussin #include <sys/types.h>
2261d06d6bSBaptiste Daroussin 
2361d06d6bSBaptiste Daroussin #include <assert.h>
2461d06d6bSBaptiste Daroussin #include <stdio.h>
2561d06d6bSBaptiste Daroussin #include <stdlib.h>
2661d06d6bSBaptiste Daroussin #include <string.h>
2761d06d6bSBaptiste Daroussin 
28*c1c95addSBrooks Davis #if DEBUG_MEMORY
29*c1c95addSBrooks Davis #include "mandoc_dbg.h"
30*c1c95addSBrooks Davis #endif
3161d06d6bSBaptiste Daroussin #include "mandoc.h"
3245a5aec3SBaptiste Daroussin #include "roff.h"
337295610fSBaptiste Daroussin #include "tbl.h"
3461d06d6bSBaptiste Daroussin #include "out.h"
3561d06d6bSBaptiste Daroussin #include "html.h"
3661d06d6bSBaptiste Daroussin 
3761d06d6bSBaptiste Daroussin static	void	 html_tblopen(struct html *, const struct tbl_span *);
3861d06d6bSBaptiste Daroussin static	size_t	 html_tbl_len(size_t, void *);
3961d06d6bSBaptiste Daroussin static	size_t	 html_tbl_strlen(const char *, void *);
4061d06d6bSBaptiste Daroussin static	size_t	 html_tbl_sulen(const struct roffsu *, void *);
4161d06d6bSBaptiste Daroussin 
4261d06d6bSBaptiste Daroussin 
4361d06d6bSBaptiste Daroussin static size_t
html_tbl_len(size_t sz,void * arg)4461d06d6bSBaptiste Daroussin html_tbl_len(size_t sz, void *arg)
4561d06d6bSBaptiste Daroussin {
4661d06d6bSBaptiste Daroussin 	return sz;
4761d06d6bSBaptiste Daroussin }
4861d06d6bSBaptiste Daroussin 
4961d06d6bSBaptiste Daroussin static size_t
html_tbl_strlen(const char * p,void * arg)5061d06d6bSBaptiste Daroussin html_tbl_strlen(const char *p, void *arg)
5161d06d6bSBaptiste Daroussin {
5261d06d6bSBaptiste Daroussin 	return strlen(p);
5361d06d6bSBaptiste Daroussin }
5461d06d6bSBaptiste Daroussin 
5561d06d6bSBaptiste Daroussin static size_t
html_tbl_sulen(const struct roffsu * su,void * arg)5661d06d6bSBaptiste Daroussin html_tbl_sulen(const struct roffsu *su, void *arg)
5761d06d6bSBaptiste Daroussin {
5861d06d6bSBaptiste Daroussin 	if (su->scale < 0.0)
5961d06d6bSBaptiste Daroussin 		return 0;
6061d06d6bSBaptiste Daroussin 
6161d06d6bSBaptiste Daroussin 	switch (su->unit) {
6261d06d6bSBaptiste Daroussin 	case SCALE_FS:  /* 2^16 basic units */
6361d06d6bSBaptiste Daroussin 		return su->scale * 65536.0 / 24.0;
6461d06d6bSBaptiste Daroussin 	case SCALE_IN:  /* 10 characters per inch */
6561d06d6bSBaptiste Daroussin 		return su->scale * 10.0;
6661d06d6bSBaptiste Daroussin 	case SCALE_CM:  /* 2.54 cm per inch */
6761d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 2.54;
6861d06d6bSBaptiste Daroussin 	case SCALE_PC:  /* 6 pica per inch */
6961d06d6bSBaptiste Daroussin 	case SCALE_VS:
7061d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 6.0;
7161d06d6bSBaptiste Daroussin 	case SCALE_EN:
7261d06d6bSBaptiste Daroussin 	case SCALE_EM:
7361d06d6bSBaptiste Daroussin 		return su->scale;
7461d06d6bSBaptiste Daroussin 	case SCALE_PT:  /* 12 points per pica */
7561d06d6bSBaptiste Daroussin 		return su->scale * 10.0 / 6.0 / 12.0;
7661d06d6bSBaptiste Daroussin 	case SCALE_BU:  /* 24 basic units per character */
7761d06d6bSBaptiste Daroussin 		return su->scale / 24.0;
7861d06d6bSBaptiste Daroussin 	case SCALE_MM:  /* 1/1000 inch */
7961d06d6bSBaptiste Daroussin 		return su->scale / 100.0;
8061d06d6bSBaptiste Daroussin 	default:
8161d06d6bSBaptiste Daroussin 		abort();
8261d06d6bSBaptiste Daroussin 	}
8361d06d6bSBaptiste Daroussin }
8461d06d6bSBaptiste Daroussin 
8561d06d6bSBaptiste Daroussin static void
html_tblopen(struct html * h,const struct tbl_span * sp)8661d06d6bSBaptiste Daroussin html_tblopen(struct html *h, const struct tbl_span *sp)
8761d06d6bSBaptiste Daroussin {
887295610fSBaptiste Daroussin 	html_close_paragraph(h);
8961d06d6bSBaptiste Daroussin 	if (h->tbl.cols == NULL) {
9061d06d6bSBaptiste Daroussin 		h->tbl.len = html_tbl_len;
9161d06d6bSBaptiste Daroussin 		h->tbl.slen = html_tbl_strlen;
9261d06d6bSBaptiste Daroussin 		h->tbl.sulen = html_tbl_sulen;
9361d06d6bSBaptiste Daroussin 		tblcalc(&h->tbl, sp, 0, 0);
9461d06d6bSBaptiste Daroussin 	}
9561d06d6bSBaptiste Daroussin 	assert(NULL == h->tblt);
967295610fSBaptiste Daroussin 	h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
977295610fSBaptiste Daroussin 	    "border",
987295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
997295610fSBaptiste Daroussin 	    "border-style",
1007295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_DBOX ? "double" :
1017295610fSBaptiste Daroussin 		sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
1027295610fSBaptiste Daroussin 	    "border-top-style",
1037295610fSBaptiste Daroussin 		sp->pos == TBL_SPAN_DHORIZ ? "double" :
1047295610fSBaptiste Daroussin 		sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
10561d06d6bSBaptiste Daroussin }
10661d06d6bSBaptiste Daroussin 
10761d06d6bSBaptiste Daroussin void
print_tblclose(struct html * h)10861d06d6bSBaptiste Daroussin print_tblclose(struct html *h)
10961d06d6bSBaptiste Daroussin {
11061d06d6bSBaptiste Daroussin 
11161d06d6bSBaptiste Daroussin 	assert(h->tblt);
11261d06d6bSBaptiste Daroussin 	print_tagq(h, h->tblt);
11361d06d6bSBaptiste Daroussin 	h->tblt = NULL;
11461d06d6bSBaptiste Daroussin }
11561d06d6bSBaptiste Daroussin 
11661d06d6bSBaptiste Daroussin void
print_tbl(struct html * h,const struct tbl_span * sp)11761d06d6bSBaptiste Daroussin print_tbl(struct html *h, const struct tbl_span *sp)
11861d06d6bSBaptiste Daroussin {
11961d06d6bSBaptiste Daroussin 	const struct tbl_dat	*dp;
1207295610fSBaptiste Daroussin 	const struct tbl_cell	*cp;
1217295610fSBaptiste Daroussin 	const struct tbl_span	*psp;
1226d38604fSBaptiste Daroussin 	const struct roffcol	*col;
12361d06d6bSBaptiste Daroussin 	struct tag		*tt;
1247295610fSBaptiste Daroussin 	const char		*hspans, *vspans, *halign, *valign;
1257295610fSBaptiste Daroussin 	const char		*bborder, *lborder, *rborder;
1266d38604fSBaptiste Daroussin 	const char		*ccp;
1277295610fSBaptiste Daroussin 	char			 hbuf[4], vbuf[4];
1286d38604fSBaptiste Daroussin 	size_t			 sz;
1296d38604fSBaptiste Daroussin 	enum mandoc_esc		 save_font;
1307295610fSBaptiste Daroussin 	int			 i;
13161d06d6bSBaptiste Daroussin 
13261d06d6bSBaptiste Daroussin 	if (h->tblt == NULL)
13361d06d6bSBaptiste Daroussin 		html_tblopen(h, sp);
13461d06d6bSBaptiste Daroussin 
1357295610fSBaptiste Daroussin 	/*
1367295610fSBaptiste Daroussin 	 * Horizontal lines spanning the whole table
1377295610fSBaptiste Daroussin 	 * are handled by previous or following table rows.
1387295610fSBaptiste Daroussin 	 */
1397295610fSBaptiste Daroussin 
1407295610fSBaptiste Daroussin 	if (sp->pos != TBL_SPAN_DATA)
141*c1c95addSBrooks Davis 		goto out;
1427295610fSBaptiste Daroussin 
1437295610fSBaptiste Daroussin 	/* Inhibit printing of spaces: we do padding ourselves. */
14461d06d6bSBaptiste Daroussin 
14561d06d6bSBaptiste Daroussin 	h->flags |= HTML_NONOSPACE;
14661d06d6bSBaptiste Daroussin 	h->flags |= HTML_NOSPACE;
14761d06d6bSBaptiste Daroussin 
1487295610fSBaptiste Daroussin 	/* Draw a vertical line left of this row? */
14961d06d6bSBaptiste Daroussin 
1507295610fSBaptiste Daroussin 	switch (sp->layout->vert) {
1517295610fSBaptiste Daroussin 	case 2:
1527295610fSBaptiste Daroussin 		lborder = "double";
1537295610fSBaptiste Daroussin 		break;
1547295610fSBaptiste Daroussin 	case 1:
1557295610fSBaptiste Daroussin 		lborder = "solid";
15661d06d6bSBaptiste Daroussin 		break;
15761d06d6bSBaptiste Daroussin 	default:
1587295610fSBaptiste Daroussin 		lborder = NULL;
1597295610fSBaptiste Daroussin 		break;
1607295610fSBaptiste Daroussin 	}
16161d06d6bSBaptiste Daroussin 
1627295610fSBaptiste Daroussin 	/* Draw a horizontal line below this row? */
1637295610fSBaptiste Daroussin 
1647295610fSBaptiste Daroussin 	bborder = NULL;
1657295610fSBaptiste Daroussin 	if ((psp = sp->next) != NULL) {
1667295610fSBaptiste Daroussin 		switch (psp->pos) {
1677295610fSBaptiste Daroussin 		case TBL_SPAN_DHORIZ:
1687295610fSBaptiste Daroussin 			bborder = "double";
1697295610fSBaptiste Daroussin 			break;
1707295610fSBaptiste Daroussin 		case TBL_SPAN_HORIZ:
1717295610fSBaptiste Daroussin 			bborder = "solid";
1727295610fSBaptiste Daroussin 			break;
1737295610fSBaptiste Daroussin 		default:
1747295610fSBaptiste Daroussin 			break;
1757295610fSBaptiste Daroussin 		}
1767295610fSBaptiste Daroussin 	}
1777295610fSBaptiste Daroussin 
1787295610fSBaptiste Daroussin 	tt = print_otag(h, TAG_TR, "ss",
1797295610fSBaptiste Daroussin 	    "border-left-style", lborder,
1807295610fSBaptiste Daroussin 	    "border-bottom-style", bborder);
1817295610fSBaptiste Daroussin 
1827295610fSBaptiste Daroussin 	for (dp = sp->first; dp != NULL; dp = dp->next) {
1837295610fSBaptiste Daroussin 		print_stagq(h, tt);
1847295610fSBaptiste Daroussin 
1857295610fSBaptiste Daroussin 		/*
1867295610fSBaptiste Daroussin 		 * Do not generate <td> elements for continuations
1877295610fSBaptiste Daroussin 		 * of spanned cells.  Larger <td> elements covering
1887295610fSBaptiste Daroussin 		 * this space were already generated earlier.
1897295610fSBaptiste Daroussin 		 */
1907295610fSBaptiste Daroussin 
1917295610fSBaptiste Daroussin 		cp = dp->layout;
1927295610fSBaptiste Daroussin 		if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
1937295610fSBaptiste Daroussin 		    (dp->string != NULL && strcmp(dp->string, "\\^") == 0))
19461d06d6bSBaptiste Daroussin 			continue;
1957295610fSBaptiste Daroussin 
1967295610fSBaptiste Daroussin 		/* Determine the attribute values. */
1977295610fSBaptiste Daroussin 
1987295610fSBaptiste Daroussin 		if (dp->hspans > 0) {
1997295610fSBaptiste Daroussin 			(void)snprintf(hbuf, sizeof(hbuf),
2007295610fSBaptiste Daroussin 			    "%d", dp->hspans + 1);
2017295610fSBaptiste Daroussin 			hspans = hbuf;
2027295610fSBaptiste Daroussin 		} else
2037295610fSBaptiste Daroussin 			hspans = NULL;
2047295610fSBaptiste Daroussin 		if (dp->vspans > 0) {
2057295610fSBaptiste Daroussin 			(void)snprintf(vbuf, sizeof(vbuf),
2067295610fSBaptiste Daroussin 			    "%d", dp->vspans + 1);
2077295610fSBaptiste Daroussin 			vspans = vbuf;
2087295610fSBaptiste Daroussin 		} else
2097295610fSBaptiste Daroussin 			vspans = NULL;
2107295610fSBaptiste Daroussin 
2117295610fSBaptiste Daroussin 		switch (cp->pos) {
2127295610fSBaptiste Daroussin 		case TBL_CELL_CENTRE:
2137295610fSBaptiste Daroussin 			halign = "center";
2147295610fSBaptiste Daroussin 			break;
2157295610fSBaptiste Daroussin 		case TBL_CELL_RIGHT:
2167295610fSBaptiste Daroussin 		case TBL_CELL_NUMBER:
2177295610fSBaptiste Daroussin 			halign = "right";
2187295610fSBaptiste Daroussin 			break;
2197295610fSBaptiste Daroussin 		default:
2207295610fSBaptiste Daroussin 			halign = NULL;
2217295610fSBaptiste Daroussin 			break;
2227295610fSBaptiste Daroussin 		}
2237295610fSBaptiste Daroussin 		if (cp->flags & TBL_CELL_TALIGN)
2247295610fSBaptiste Daroussin 			valign = "top";
2257295610fSBaptiste Daroussin 		else if (cp->flags & TBL_CELL_BALIGN)
2267295610fSBaptiste Daroussin 			valign = "bottom";
2277295610fSBaptiste Daroussin 		else
2287295610fSBaptiste Daroussin 			valign = NULL;
2297295610fSBaptiste Daroussin 
2307295610fSBaptiste Daroussin 		for (i = dp->hspans; i > 0; i--)
2317295610fSBaptiste Daroussin 			cp = cp->next;
2327295610fSBaptiste Daroussin 		switch (cp->vert) {
2337295610fSBaptiste Daroussin 		case 2:
2347295610fSBaptiste Daroussin 			rborder = "double";
2357295610fSBaptiste Daroussin 			break;
2367295610fSBaptiste Daroussin 		case 1:
2377295610fSBaptiste Daroussin 			rborder = "solid";
2387295610fSBaptiste Daroussin 			break;
2397295610fSBaptiste Daroussin 		default:
2407295610fSBaptiste Daroussin 			rborder = NULL;
2417295610fSBaptiste Daroussin 			break;
2427295610fSBaptiste Daroussin 		}
2437295610fSBaptiste Daroussin 
2447295610fSBaptiste Daroussin 		/* Print the element and the attributes. */
2457295610fSBaptiste Daroussin 
2467295610fSBaptiste Daroussin 		print_otag(h, TAG_TD, "??sss",
2477295610fSBaptiste Daroussin 		    "colspan", hspans, "rowspan", vspans,
2487295610fSBaptiste Daroussin 		    "vertical-align", valign,
2497295610fSBaptiste Daroussin 		    "text-align", halign,
2507295610fSBaptiste Daroussin 		    "border-right-style", rborder);
2516d38604fSBaptiste Daroussin 		if (dp->layout->pos == TBL_CELL_HORIZ ||
2526d38604fSBaptiste Daroussin 		    dp->layout->pos == TBL_CELL_DHORIZ ||
2536d38604fSBaptiste Daroussin 		    dp->pos == TBL_DATA_HORIZ ||
254*c1c95addSBrooks Davis 		    dp->pos == TBL_DATA_NHORIZ ||
255*c1c95addSBrooks Davis 		    dp->pos == TBL_DATA_DHORIZ ||
256*c1c95addSBrooks Davis 		    dp->pos == TBL_DATA_NDHORIZ)
2576d38604fSBaptiste Daroussin 			print_otag(h, TAG_HR, "");
2586d38604fSBaptiste Daroussin 		else if (dp->string != NULL) {
2596d38604fSBaptiste Daroussin 			save_font = h->metac;
2606d38604fSBaptiste Daroussin 			html_setfont(h, dp->layout->font);
2616d38604fSBaptiste Daroussin 			if (dp->layout->pos == TBL_CELL_LONG)
2626d38604fSBaptiste Daroussin 				print_text(h, "\\[u2003]");  /* em space */
26361d06d6bSBaptiste Daroussin 			print_text(h, dp->string);
2646d38604fSBaptiste Daroussin 			if (dp->layout->pos == TBL_CELL_NUMBER) {
2656d38604fSBaptiste Daroussin 				col = h->tbl.cols + dp->layout->col;
2666d38604fSBaptiste Daroussin 				if (col->decimal < col->nwidth) {
2676d38604fSBaptiste Daroussin 					if ((ccp = strrchr(dp->string,
2686d38604fSBaptiste Daroussin 					    sp->opts->decimal)) == NULL) {
2696d38604fSBaptiste Daroussin 						/* Punctuation space. */
2706d38604fSBaptiste Daroussin 						print_text(h, "\\[u2008]");
2716d38604fSBaptiste Daroussin 						ccp = strchr(dp->string, '\0');
2726d38604fSBaptiste Daroussin 					} else
2736d38604fSBaptiste Daroussin 						ccp++;
2746d38604fSBaptiste Daroussin 					sz = col->nwidth - col->decimal;
2756d38604fSBaptiste Daroussin 					while (--sz > 0) {
2766d38604fSBaptiste Daroussin 						if (*ccp == '\0')
2776d38604fSBaptiste Daroussin 							/* Figure space. */
2786d38604fSBaptiste Daroussin 							print_text(h,
2796d38604fSBaptiste Daroussin 							    "\\[u2007]");
2806d38604fSBaptiste Daroussin 						else
2816d38604fSBaptiste Daroussin 							ccp++;
2826d38604fSBaptiste Daroussin 					}
2836d38604fSBaptiste Daroussin 				}
2846d38604fSBaptiste Daroussin 			}
2856d38604fSBaptiste Daroussin 			html_setfont(h, save_font);
2866d38604fSBaptiste Daroussin 		}
28761d06d6bSBaptiste Daroussin 	}
28861d06d6bSBaptiste Daroussin 
28961d06d6bSBaptiste Daroussin 	print_tagq(h, tt);
29061d06d6bSBaptiste Daroussin 
29161d06d6bSBaptiste Daroussin 	h->flags &= ~HTML_NONOSPACE;
29261d06d6bSBaptiste Daroussin 
293*c1c95addSBrooks Davis out:
29461d06d6bSBaptiste Daroussin 	if (sp->next == NULL) {
29561d06d6bSBaptiste Daroussin 		assert(h->tbl.cols);
29661d06d6bSBaptiste Daroussin 		free(h->tbl.cols);
29761d06d6bSBaptiste Daroussin 		h->tbl.cols = NULL;
29861d06d6bSBaptiste Daroussin 		print_tblclose(h);
29961d06d6bSBaptiste Daroussin 	}
30061d06d6bSBaptiste Daroussin }
301