xref: /openbsd-src/usr.bin/mandoc/tbl_html.c (revision 82de296fc707203047097cc38aa5ca158e277435)
1*82de296fSschwarze /* $OpenBSD: tbl_html.c,v 1.35 2022/04/23 13:58:09 schwarze Exp $ */
22791bd1cSschwarze /*
3*82de296fSschwarze  * Copyright (c) 2014, 2015, 2017, 2018, 2021, 2022
4*82de296fSschwarze  *               Ingo Schwarze <schwarze@openbsd.org>
504e980cbSschwarze  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
62791bd1cSschwarze  *
72791bd1cSschwarze  * Permission to use, copy, modify, and distribute this software for any
82791bd1cSschwarze  * purpose with or without fee is hereby granted, provided that the above
92791bd1cSschwarze  * copyright notice and this permission notice appear in all copies.
102791bd1cSschwarze  *
112791bd1cSschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
122791bd1cSschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
132791bd1cSschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
142791bd1cSschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
152791bd1cSschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
162791bd1cSschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
172791bd1cSschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
182791bd1cSschwarze  */
1946fa2066Sschwarze #include <sys/types.h>
2046fa2066Sschwarze 
212791bd1cSschwarze #include <assert.h>
222791bd1cSschwarze #include <stdio.h>
232791bd1cSschwarze #include <stdlib.h>
242791bd1cSschwarze #include <string.h>
252791bd1cSschwarze 
262e362670Sschwarze #include "mandoc.h"
278761b3c5Sschwarze #include "roff.h"
28fae2491eSschwarze #include "tbl.h"
292791bd1cSschwarze #include "out.h"
302791bd1cSschwarze #include "html.h"
312791bd1cSschwarze 
32366f22eeSschwarze static	void	 html_tblopen(struct html *, const struct tbl_span *);
33ec04407bSschwarze static	size_t	 html_tbl_len(size_t, void *);
34ec04407bSschwarze static	size_t	 html_tbl_strlen(const char *, void *);
352c3e66c4Sschwarze static	size_t	 html_tbl_sulen(const struct roffsu *, void *);
36ec04407bSschwarze 
3749aff9f8Sschwarze 
38ec04407bSschwarze static size_t
html_tbl_len(size_t sz,void * arg)39ec04407bSschwarze html_tbl_len(size_t sz, void *arg)
40ec04407bSschwarze {
41526e306bSschwarze 	return sz;
42ec04407bSschwarze }
43ec04407bSschwarze 
44ec04407bSschwarze static size_t
html_tbl_strlen(const char * p,void * arg)45ec04407bSschwarze html_tbl_strlen(const char *p, void *arg)
46ec04407bSschwarze {
47526e306bSschwarze 	return strlen(p);
48ec04407bSschwarze }
49ec04407bSschwarze 
502c3e66c4Sschwarze static size_t
html_tbl_sulen(const struct roffsu * su,void * arg)512c3e66c4Sschwarze html_tbl_sulen(const struct roffsu *su, void *arg)
522c3e66c4Sschwarze {
534fcba843Sschwarze 	if (su->scale < 0.0)
544fcba843Sschwarze 		return 0;
554fcba843Sschwarze 
562c3e66c4Sschwarze 	switch (su->unit) {
572c3e66c4Sschwarze 	case SCALE_FS:  /* 2^16 basic units */
582c3e66c4Sschwarze 		return su->scale * 65536.0 / 24.0;
592c3e66c4Sschwarze 	case SCALE_IN:  /* 10 characters per inch */
602c3e66c4Sschwarze 		return su->scale * 10.0;
612c3e66c4Sschwarze 	case SCALE_CM:  /* 2.54 cm per inch */
622c3e66c4Sschwarze 		return su->scale * 10.0 / 2.54;
632c3e66c4Sschwarze 	case SCALE_PC:  /* 6 pica per inch */
642c3e66c4Sschwarze 	case SCALE_VS:
652c3e66c4Sschwarze 		return su->scale * 10.0 / 6.0;
662c3e66c4Sschwarze 	case SCALE_EN:
672c3e66c4Sschwarze 	case SCALE_EM:
682c3e66c4Sschwarze 		return su->scale;
692c3e66c4Sschwarze 	case SCALE_PT:  /* 12 points per pica */
702c3e66c4Sschwarze 		return su->scale * 10.0 / 6.0 / 12.0;
712c3e66c4Sschwarze 	case SCALE_BU:  /* 24 basic units per character */
722c3e66c4Sschwarze 		return su->scale / 24.0;
732c3e66c4Sschwarze 	case SCALE_MM:  /* 1/1000 inch */
742c3e66c4Sschwarze 		return su->scale / 100.0;
752c3e66c4Sschwarze 	default:
762c3e66c4Sschwarze 		abort();
772c3e66c4Sschwarze 	}
782c3e66c4Sschwarze }
792c3e66c4Sschwarze 
80366f22eeSschwarze static void
html_tblopen(struct html * h,const struct tbl_span * sp)81366f22eeSschwarze html_tblopen(struct html *h, const struct tbl_span *sp)
822791bd1cSschwarze {
837f442bfeSschwarze 	html_close_paragraph(h);
84cb151596Sschwarze 	if (h->tbl.cols == NULL) {
85ec04407bSschwarze 		h->tbl.len = html_tbl_len;
86ec04407bSschwarze 		h->tbl.slen = html_tbl_strlen;
872c3e66c4Sschwarze 		h->tbl.sulen = html_tbl_sulen;
88d1c3ec49Sschwarze 		tblcalc(&h->tbl, sp, 0, 0);
89ec04407bSschwarze 	}
90366f22eeSschwarze 	assert(NULL == h->tblt);
915e80c341Sschwarze 	h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
925e80c341Sschwarze 	    "border",
935e80c341Sschwarze 		sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
945e80c341Sschwarze 	    "border-style",
955e80c341Sschwarze 		sp->opts->opts & TBL_OPT_DBOX ? "double" :
965e80c341Sschwarze 		sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
975e80c341Sschwarze 	    "border-top-style",
985e80c341Sschwarze 		sp->pos == TBL_SPAN_DHORIZ ? "double" :
995e80c341Sschwarze 		sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
100366f22eeSschwarze }
101366f22eeSschwarze 
102366f22eeSschwarze void
print_tblclose(struct html * h)103366f22eeSschwarze print_tblclose(struct html *h)
104366f22eeSschwarze {
105366f22eeSschwarze 
106366f22eeSschwarze 	assert(h->tblt);
107366f22eeSschwarze 	print_tagq(h, h->tblt);
108366f22eeSschwarze 	h->tblt = NULL;
109366f22eeSschwarze }
110366f22eeSschwarze 
111366f22eeSschwarze void
print_tbl(struct html * h,const struct tbl_span * sp)112366f22eeSschwarze print_tbl(struct html *h, const struct tbl_span *sp)
113366f22eeSschwarze {
114366f22eeSschwarze 	const struct tbl_dat	*dp;
1155e80c341Sschwarze 	const struct tbl_cell	*cp;
1165e80c341Sschwarze 	const struct tbl_span	*psp;
1171fe41204Sschwarze 	const struct roffcol	*col;
118366f22eeSschwarze 	struct tag		*tt;
119a93944b2Sschwarze 	const char		*hspans, *vspans, *halign, *valign;
1205e80c341Sschwarze 	const char		*bborder, *lborder, *rborder;
1211fe41204Sschwarze 	const char		*ccp;
122a93944b2Sschwarze 	char			 hbuf[4], vbuf[4];
1231fe41204Sschwarze 	size_t			 sz;
12407460cefSschwarze 	enum mandoc_esc		 save_font;
1255e80c341Sschwarze 	int			 i;
126366f22eeSschwarze 
12721da0636Sschwarze 	if (h->tblt == NULL)
128366f22eeSschwarze 		html_tblopen(h, sp);
129366f22eeSschwarze 
1305e80c341Sschwarze 	/*
1315e80c341Sschwarze 	 * Horizontal lines spanning the whole table
1325e80c341Sschwarze 	 * are handled by previous or following table rows.
1335e80c341Sschwarze 	 */
1345e80c341Sschwarze 
1355e80c341Sschwarze 	if (sp->pos != TBL_SPAN_DATA)
136*82de296fSschwarze 		goto out;
1375e80c341Sschwarze 
1385e80c341Sschwarze 	/* Inhibit printing of spaces: we do padding ourselves. */
139366f22eeSschwarze 
140366f22eeSschwarze 	h->flags |= HTML_NONOSPACE;
141366f22eeSschwarze 	h->flags |= HTML_NOSPACE;
142366f22eeSschwarze 
1435e80c341Sschwarze 	/* Draw a vertical line left of this row? */
144366f22eeSschwarze 
1455e80c341Sschwarze 	switch (sp->layout->vert) {
1465e80c341Sschwarze 	case 2:
1475e80c341Sschwarze 		lborder = "double";
1485e80c341Sschwarze 		break;
1495e80c341Sschwarze 	case 1:
1505e80c341Sschwarze 		lborder = "solid";
151ec04407bSschwarze 		break;
152ec04407bSschwarze 	default:
1535e80c341Sschwarze 		lborder = NULL;
1545e80c341Sschwarze 		break;
1555e80c341Sschwarze 	}
1565e80c341Sschwarze 
1575e80c341Sschwarze 	/* Draw a horizontal line below this row? */
1585e80c341Sschwarze 
1595e80c341Sschwarze 	bborder = NULL;
1605e80c341Sschwarze 	if ((psp = sp->next) != NULL) {
1615e80c341Sschwarze 		switch (psp->pos) {
1625e80c341Sschwarze 		case TBL_SPAN_DHORIZ:
1635e80c341Sschwarze 			bborder = "double";
1645e80c341Sschwarze 			break;
1655e80c341Sschwarze 		case TBL_SPAN_HORIZ:
1665e80c341Sschwarze 			bborder = "solid";
1675e80c341Sschwarze 			break;
1685e80c341Sschwarze 		default:
1695e80c341Sschwarze 			break;
1705e80c341Sschwarze 		}
1715e80c341Sschwarze 	}
1725e80c341Sschwarze 
1735e80c341Sschwarze 	tt = print_otag(h, TAG_TR, "ss",
1745e80c341Sschwarze 	    "border-left-style", lborder,
1755e80c341Sschwarze 	    "border-bottom-style", bborder);
1765e80c341Sschwarze 
177a93944b2Sschwarze 	for (dp = sp->first; dp != NULL; dp = dp->next) {
178366f22eeSschwarze 		print_stagq(h, tt);
1797a5a8f14Sschwarze 
1807a5a8f14Sschwarze 		/*
1817a5a8f14Sschwarze 		 * Do not generate <td> elements for continuations
1827a5a8f14Sschwarze 		 * of spanned cells.  Larger <td> elements covering
1837a5a8f14Sschwarze 		 * this space were already generated earlier.
1847a5a8f14Sschwarze 		 */
1857a5a8f14Sschwarze 
1865e80c341Sschwarze 		cp = dp->layout;
1875e80c341Sschwarze 		if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
1885e80c341Sschwarze 		    (dp->string != NULL && strcmp(dp->string, "\\^") == 0))
1895f6d1ba3Sschwarze 			continue;
190a93944b2Sschwarze 
191a93944b2Sschwarze 		/* Determine the attribute values. */
192a93944b2Sschwarze 
193a93944b2Sschwarze 		if (dp->hspans > 0) {
194a93944b2Sschwarze 			(void)snprintf(hbuf, sizeof(hbuf),
195a93944b2Sschwarze 			    "%d", dp->hspans + 1);
196a93944b2Sschwarze 			hspans = hbuf;
197a93944b2Sschwarze 		} else
198a93944b2Sschwarze 			hspans = NULL;
199a93944b2Sschwarze 		if (dp->vspans > 0) {
200a93944b2Sschwarze 			(void)snprintf(vbuf, sizeof(vbuf),
201a93944b2Sschwarze 			    "%d", dp->vspans + 1);
202a93944b2Sschwarze 			vspans = vbuf;
203a93944b2Sschwarze 		} else
204a93944b2Sschwarze 			vspans = NULL;
205a93944b2Sschwarze 
2065e80c341Sschwarze 		switch (cp->pos) {
2076bcc7ca8Sschwarze 		case TBL_CELL_CENTRE:
2086bcc7ca8Sschwarze 			halign = "center";
2096bcc7ca8Sschwarze 			break;
2106bcc7ca8Sschwarze 		case TBL_CELL_RIGHT:
2116bcc7ca8Sschwarze 		case TBL_CELL_NUMBER:
2126bcc7ca8Sschwarze 			halign = "right";
2136bcc7ca8Sschwarze 			break;
2146bcc7ca8Sschwarze 		default:
2156bcc7ca8Sschwarze 			halign = NULL;
2166bcc7ca8Sschwarze 			break;
2176bcc7ca8Sschwarze 		}
2185e80c341Sschwarze 		if (cp->flags & TBL_CELL_TALIGN)
2196bcc7ca8Sschwarze 			valign = "top";
2205e80c341Sschwarze 		else if (cp->flags & TBL_CELL_BALIGN)
2216bcc7ca8Sschwarze 			valign = "bottom";
2226bcc7ca8Sschwarze 		else
2236bcc7ca8Sschwarze 			valign = NULL;
224a93944b2Sschwarze 
2255e80c341Sschwarze 		for (i = dp->hspans; i > 0; i--)
2265e80c341Sschwarze 			cp = cp->next;
2275e80c341Sschwarze 		switch (cp->vert) {
2285e80c341Sschwarze 		case 2:
2295e80c341Sschwarze 			rborder = "double";
2305e80c341Sschwarze 			break;
2315e80c341Sschwarze 		case 1:
2325e80c341Sschwarze 			rborder = "solid";
2335e80c341Sschwarze 			break;
2345e80c341Sschwarze 		default:
2355e80c341Sschwarze 			rborder = NULL;
2365e80c341Sschwarze 			break;
2375e80c341Sschwarze 		}
2385e80c341Sschwarze 
239a93944b2Sschwarze 		/* Print the element and the attributes. */
240a93944b2Sschwarze 
2415e80c341Sschwarze 		print_otag(h, TAG_TD, "??sss",
242a93944b2Sschwarze 		    "colspan", hspans, "rowspan", vspans,
2436bcc7ca8Sschwarze 		    "vertical-align", valign,
2445e80c341Sschwarze 		    "text-align", halign,
2455e80c341Sschwarze 		    "border-right-style", rborder);
2461ae734e3Sschwarze 		if (dp->layout->pos == TBL_CELL_HORIZ ||
2471ae734e3Sschwarze 		    dp->layout->pos == TBL_CELL_DHORIZ ||
2481ae734e3Sschwarze 		    dp->pos == TBL_DATA_HORIZ ||
249fa3f0bd5Sschwarze 		    dp->pos == TBL_DATA_NHORIZ ||
250fa3f0bd5Sschwarze 		    dp->pos == TBL_DATA_DHORIZ ||
251fa3f0bd5Sschwarze 		    dp->pos == TBL_DATA_NDHORIZ)
2521ae734e3Sschwarze 			print_otag(h, TAG_HR, "");
2531ae734e3Sschwarze 		else if (dp->string != NULL) {
25407460cefSschwarze 			save_font = h->metac;
2557d063611Sschwarze 			html_setfont(h, dp->layout->font);
256d4069642Sschwarze 			if (dp->layout->pos == TBL_CELL_LONG)
257d4069642Sschwarze 				print_text(h, "\\[u2003]");  /* em space */
2582791bd1cSschwarze 			print_text(h, dp->string);
2591fe41204Sschwarze 			if (dp->layout->pos == TBL_CELL_NUMBER) {
2601fe41204Sschwarze 				col = h->tbl.cols + dp->layout->col;
2611fe41204Sschwarze 				if (col->decimal < col->nwidth) {
2621fe41204Sschwarze 					if ((ccp = strrchr(dp->string,
2631fe41204Sschwarze 					    sp->opts->decimal)) == NULL) {
2641fe41204Sschwarze 						/* Punctuation space. */
2651fe41204Sschwarze 						print_text(h, "\\[u2008]");
2661fe41204Sschwarze 						ccp = strchr(dp->string, '\0');
2671fe41204Sschwarze 					} else
2681fe41204Sschwarze 						ccp++;
2691fe41204Sschwarze 					sz = col->nwidth - col->decimal;
2701fe41204Sschwarze 					while (--sz > 0) {
2711fe41204Sschwarze 						if (*ccp == '\0')
2721fe41204Sschwarze 							/* Figure space. */
2731fe41204Sschwarze 							print_text(h,
2741fe41204Sschwarze 							    "\\[u2007]");
2751fe41204Sschwarze 						else
2761fe41204Sschwarze 							ccp++;
2771fe41204Sschwarze 					}
2781fe41204Sschwarze 				}
2791fe41204Sschwarze 			}
28007460cefSschwarze 			html_setfont(h, save_font);
28107460cefSschwarze 		}
282366f22eeSschwarze 	}
283ec04407bSschwarze 
2842791bd1cSschwarze 	print_tagq(h, tt);
285ec04407bSschwarze 
2862791bd1cSschwarze 	h->flags &= ~HTML_NONOSPACE;
287ec04407bSschwarze 
288*82de296fSschwarze out:
289cb151596Sschwarze 	if (sp->next == NULL) {
290ec04407bSschwarze 		assert(h->tbl.cols);
291ec04407bSschwarze 		free(h->tbl.cols);
292ec04407bSschwarze 		h->tbl.cols = NULL;
293366f22eeSschwarze 		print_tblclose(h);
294ec04407bSschwarze 	}
2952791bd1cSschwarze }
296