xref: /illumos-gate/usr/src/cmd/mandoc/tbl_html.c (revision 4d131170)
1*4d131170SRobert Mustacchi /*	$Id: tbl_html.c,v 1.38 2021/09/09 16:52:52 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*4d131170SRobert Mustacchi  * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
19260e9a87SYuri Pankov 
20260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <stdio.h>
2495c635efSGarrett D'Amore #include <stdlib.h>
2595c635efSGarrett D'Amore #include <string.h>
2695c635efSGarrett D'Amore 
2795c635efSGarrett D'Amore #include "mandoc.h"
28*4d131170SRobert Mustacchi #include "roff.h"
29cec8643bSMichal Nowak #include "tbl.h"
3095c635efSGarrett D'Amore #include "out.h"
3195c635efSGarrett D'Amore #include "html.h"
3295c635efSGarrett D'Amore 
3395c635efSGarrett D'Amore static	void	 html_tblopen(struct html *, const struct tbl_span *);
3495c635efSGarrett D'Amore static	size_t	 html_tbl_len(size_t, void *);
3595c635efSGarrett D'Amore static	size_t	 html_tbl_strlen(const char *, void *);
36c66b8046SYuri Pankov static	size_t	 html_tbl_sulen(const struct roffsu *, void *);
3795c635efSGarrett D'Amore 
38260e9a87SYuri Pankov 
3995c635efSGarrett D'Amore static size_t
html_tbl_len(size_t sz,void * arg)4095c635efSGarrett D'Amore html_tbl_len(size_t sz, void *arg)
4195c635efSGarrett D'Amore {
42371584c2SYuri Pankov 	return sz;
4395c635efSGarrett D'Amore }
4495c635efSGarrett D'Amore 
4595c635efSGarrett D'Amore static size_t
html_tbl_strlen(const char * p,void * arg)4695c635efSGarrett D'Amore html_tbl_strlen(const char *p, void *arg)
4795c635efSGarrett D'Amore {
48371584c2SYuri Pankov 	return strlen(p);
4995c635efSGarrett D'Amore }
5095c635efSGarrett D'Amore 
51c66b8046SYuri Pankov static size_t
html_tbl_sulen(const struct roffsu * su,void * arg)52c66b8046SYuri Pankov html_tbl_sulen(const struct roffsu *su, void *arg)
53c66b8046SYuri Pankov {
54c66b8046SYuri Pankov 	if (su->scale < 0.0)
55c66b8046SYuri Pankov 		return 0;
56c66b8046SYuri Pankov 
57c66b8046SYuri Pankov 	switch (su->unit) {
58c66b8046SYuri Pankov 	case SCALE_FS:  /* 2^16 basic units */
59c66b8046SYuri Pankov 		return su->scale * 65536.0 / 24.0;
60c66b8046SYuri Pankov 	case SCALE_IN:  /* 10 characters per inch */
61c66b8046SYuri Pankov 		return su->scale * 10.0;
62c66b8046SYuri Pankov 	case SCALE_CM:  /* 2.54 cm per inch */
63c66b8046SYuri Pankov 		return su->scale * 10.0 / 2.54;
64c66b8046SYuri Pankov 	case SCALE_PC:  /* 6 pica per inch */
65c66b8046SYuri Pankov 	case SCALE_VS:
66c66b8046SYuri Pankov 		return su->scale * 10.0 / 6.0;
67c66b8046SYuri Pankov 	case SCALE_EN:
68c66b8046SYuri Pankov 	case SCALE_EM:
69c66b8046SYuri Pankov 		return su->scale;
70c66b8046SYuri Pankov 	case SCALE_PT:  /* 12 points per pica */
71c66b8046SYuri Pankov 		return su->scale * 10.0 / 6.0 / 12.0;
72c66b8046SYuri Pankov 	case SCALE_BU:  /* 24 basic units per character */
73c66b8046SYuri Pankov 		return su->scale / 24.0;
74c66b8046SYuri Pankov 	case SCALE_MM:  /* 1/1000 inch */
75c66b8046SYuri Pankov 		return su->scale / 100.0;
76c66b8046SYuri Pankov 	default:
77c66b8046SYuri Pankov 		abort();
78c66b8046SYuri Pankov 	}
79c66b8046SYuri Pankov }
80c66b8046SYuri Pankov 
8195c635efSGarrett D'Amore static void
html_tblopen(struct html * h,const struct tbl_span * sp)8295c635efSGarrett D'Amore html_tblopen(struct html *h, const struct tbl_span *sp)
8395c635efSGarrett D'Amore {
84cec8643bSMichal Nowak 	html_close_paragraph(h);
85260e9a87SYuri Pankov 	if (h->tbl.cols == NULL) {
8695c635efSGarrett D'Amore 		h->tbl.len = html_tbl_len;
8795c635efSGarrett D'Amore 		h->tbl.slen = html_tbl_strlen;
88c66b8046SYuri Pankov 		h->tbl.sulen = html_tbl_sulen;
89c66b8046SYuri Pankov 		tblcalc(&h->tbl, sp, 0, 0);
9095c635efSGarrett D'Amore 	}
9195c635efSGarrett D'Amore 	assert(NULL == h->tblt);
92cec8643bSMichal Nowak 	h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl",
93cec8643bSMichal Nowak 	    "border",
94cec8643bSMichal Nowak 		sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL,
95cec8643bSMichal Nowak 	    "border-style",
96cec8643bSMichal Nowak 		sp->opts->opts & TBL_OPT_DBOX ? "double" :
97cec8643bSMichal Nowak 		sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL,
98cec8643bSMichal Nowak 	    "border-top-style",
99cec8643bSMichal Nowak 		sp->pos == TBL_SPAN_DHORIZ ? "double" :
100cec8643bSMichal Nowak 		sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL);
10195c635efSGarrett D'Amore }
10295c635efSGarrett D'Amore 
10395c635efSGarrett D'Amore void
print_tblclose(struct html * h)10495c635efSGarrett D'Amore print_tblclose(struct html *h)
10595c635efSGarrett D'Amore {
10695c635efSGarrett D'Amore 
10795c635efSGarrett D'Amore 	assert(h->tblt);
10895c635efSGarrett D'Amore 	print_tagq(h, h->tblt);
10995c635efSGarrett D'Amore 	h->tblt = NULL;
11095c635efSGarrett D'Amore }
11195c635efSGarrett D'Amore 
11295c635efSGarrett D'Amore void
print_tbl(struct html * h,const struct tbl_span * sp)11395c635efSGarrett D'Amore print_tbl(struct html *h, const struct tbl_span *sp)
11495c635efSGarrett D'Amore {
115cec8643bSMichal Nowak 	const struct tbl_dat	*dp;
116cec8643bSMichal Nowak 	const struct tbl_cell	*cp;
117cec8643bSMichal Nowak 	const struct tbl_span	*psp;
118*4d131170SRobert Mustacchi 	const struct roffcol	*col;
119cec8643bSMichal Nowak 	struct tag		*tt;
120cec8643bSMichal Nowak 	const char		*hspans, *vspans, *halign, *valign;
121cec8643bSMichal Nowak 	const char		*bborder, *lborder, *rborder;
122*4d131170SRobert Mustacchi 	const char		*ccp;
123cec8643bSMichal Nowak 	char			 hbuf[4], vbuf[4];
124*4d131170SRobert Mustacchi 	size_t			 sz;
125*4d131170SRobert Mustacchi 	enum mandoc_esc		 save_font;
126cec8643bSMichal Nowak 	int			 i;
12795c635efSGarrett D'Amore 
128260e9a87SYuri Pankov 	if (h->tblt == NULL)
12995c635efSGarrett D'Amore 		html_tblopen(h, sp);
13095c635efSGarrett D'Amore 
131cec8643bSMichal Nowak 	/*
132cec8643bSMichal Nowak 	 * Horizontal lines spanning the whole table
133cec8643bSMichal Nowak 	 * are handled by previous or following table rows.
134cec8643bSMichal Nowak 	 */
135cec8643bSMichal Nowak 
136cec8643bSMichal Nowak 	if (sp->pos != TBL_SPAN_DATA)
137cec8643bSMichal Nowak 		return;
138cec8643bSMichal Nowak 
139cec8643bSMichal Nowak 	/* Inhibit printing of spaces: we do padding ourselves. */
14095c635efSGarrett D'Amore 
14195c635efSGarrett D'Amore 	h->flags |= HTML_NONOSPACE;
14295c635efSGarrett D'Amore 	h->flags |= HTML_NOSPACE;
14395c635efSGarrett D'Amore 
144cec8643bSMichal Nowak 	/* Draw a vertical line left of this row? */
14595c635efSGarrett D'Amore 
146cec8643bSMichal Nowak 	switch (sp->layout->vert) {
147cec8643bSMichal Nowak 	case 2:
148cec8643bSMichal Nowak 		lborder = "double";
149cec8643bSMichal Nowak 		break;
150cec8643bSMichal Nowak 	case 1:
151cec8643bSMichal Nowak 		lborder = "solid";
15295c635efSGarrett D'Amore 		break;
15395c635efSGarrett D'Amore 	default:
154cec8643bSMichal Nowak 		lborder = NULL;
15595c635efSGarrett D'Amore 		break;
15695c635efSGarrett D'Amore 	}
15795c635efSGarrett D'Amore 
158cec8643bSMichal Nowak 	/* Draw a horizontal line below this row? */
159cec8643bSMichal Nowak 
160cec8643bSMichal Nowak 	bborder = NULL;
161cec8643bSMichal Nowak 	if ((psp = sp->next) != NULL) {
162cec8643bSMichal Nowak 		switch (psp->pos) {
163cec8643bSMichal Nowak 		case TBL_SPAN_DHORIZ:
164cec8643bSMichal Nowak 			bborder = "double";
165cec8643bSMichal Nowak 			break;
166cec8643bSMichal Nowak 		case TBL_SPAN_HORIZ:
167cec8643bSMichal Nowak 			bborder = "solid";
168cec8643bSMichal Nowak 			break;
169cec8643bSMichal Nowak 		default:
170cec8643bSMichal Nowak 			break;
171cec8643bSMichal Nowak 		}
172cec8643bSMichal Nowak 	}
173cec8643bSMichal Nowak 
174cec8643bSMichal Nowak 	tt = print_otag(h, TAG_TR, "ss",
175cec8643bSMichal Nowak 	    "border-left-style", lborder,
176cec8643bSMichal Nowak 	    "border-bottom-style", bborder);
177cec8643bSMichal Nowak 
178cec8643bSMichal Nowak 	for (dp = sp->first; dp != NULL; dp = dp->next) {
179cec8643bSMichal Nowak 		print_stagq(h, tt);
180cec8643bSMichal Nowak 
181cec8643bSMichal Nowak 		/*
182cec8643bSMichal Nowak 		 * Do not generate <td> elements for continuations
183cec8643bSMichal Nowak 		 * of spanned cells.  Larger <td> elements covering
184cec8643bSMichal Nowak 		 * this space were already generated earlier.
185cec8643bSMichal Nowak 		 */
186cec8643bSMichal Nowak 
187cec8643bSMichal Nowak 		cp = dp->layout;
188cec8643bSMichal Nowak 		if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN ||
189cec8643bSMichal Nowak 		    (dp->string != NULL && strcmp(dp->string, "\\^") == 0))
190cec8643bSMichal Nowak 			continue;
191cec8643bSMichal Nowak 
192cec8643bSMichal Nowak 		/* Determine the attribute values. */
193cec8643bSMichal Nowak 
194cec8643bSMichal Nowak 		if (dp->hspans > 0) {
195cec8643bSMichal Nowak 			(void)snprintf(hbuf, sizeof(hbuf),
196cec8643bSMichal Nowak 			    "%d", dp->hspans + 1);
197cec8643bSMichal Nowak 			hspans = hbuf;
198cec8643bSMichal Nowak 		} else
199cec8643bSMichal Nowak 			hspans = NULL;
200cec8643bSMichal Nowak 		if (dp->vspans > 0) {
201cec8643bSMichal Nowak 			(void)snprintf(vbuf, sizeof(vbuf),
202cec8643bSMichal Nowak 			    "%d", dp->vspans + 1);
203cec8643bSMichal Nowak 			vspans = vbuf;
204cec8643bSMichal Nowak 		} else
205cec8643bSMichal Nowak 			vspans = NULL;
206cec8643bSMichal Nowak 
207cec8643bSMichal Nowak 		switch (cp->pos) {
208cec8643bSMichal Nowak 		case TBL_CELL_CENTRE:
209cec8643bSMichal Nowak 			halign = "center";
210cec8643bSMichal Nowak 			break;
211cec8643bSMichal Nowak 		case TBL_CELL_RIGHT:
212cec8643bSMichal Nowak 		case TBL_CELL_NUMBER:
213cec8643bSMichal Nowak 			halign = "right";
214cec8643bSMichal Nowak 			break;
215cec8643bSMichal Nowak 		default:
216cec8643bSMichal Nowak 			halign = NULL;
217cec8643bSMichal Nowak 			break;
218cec8643bSMichal Nowak 		}
219cec8643bSMichal Nowak 		if (cp->flags & TBL_CELL_TALIGN)
220cec8643bSMichal Nowak 			valign = "top";
221cec8643bSMichal Nowak 		else if (cp->flags & TBL_CELL_BALIGN)
222cec8643bSMichal Nowak 			valign = "bottom";
223cec8643bSMichal Nowak 		else
224cec8643bSMichal Nowak 			valign = NULL;
225cec8643bSMichal Nowak 
226cec8643bSMichal Nowak 		for (i = dp->hspans; i > 0; i--)
227cec8643bSMichal Nowak 			cp = cp->next;
228cec8643bSMichal Nowak 		switch (cp->vert) {
229cec8643bSMichal Nowak 		case 2:
230cec8643bSMichal Nowak 			rborder = "double";
231cec8643bSMichal Nowak 			break;
232cec8643bSMichal Nowak 		case 1:
233cec8643bSMichal Nowak 			rborder = "solid";
234cec8643bSMichal Nowak 			break;
235cec8643bSMichal Nowak 		default:
236cec8643bSMichal Nowak 			rborder = NULL;
237cec8643bSMichal Nowak 			break;
238cec8643bSMichal Nowak 		}
239cec8643bSMichal Nowak 
240cec8643bSMichal Nowak 		/* Print the element and the attributes. */
241cec8643bSMichal Nowak 
242cec8643bSMichal Nowak 		print_otag(h, TAG_TD, "??sss",
243cec8643bSMichal Nowak 		    "colspan", hspans, "rowspan", vspans,
244cec8643bSMichal Nowak 		    "vertical-align", valign,
245cec8643bSMichal Nowak 		    "text-align", halign,
246cec8643bSMichal Nowak 		    "border-right-style", rborder);
247*4d131170SRobert Mustacchi 		if (dp->layout->pos == TBL_CELL_HORIZ ||
248*4d131170SRobert Mustacchi 		    dp->layout->pos == TBL_CELL_DHORIZ ||
249*4d131170SRobert Mustacchi 		    dp->pos == TBL_DATA_HORIZ ||
250*4d131170SRobert Mustacchi 		    dp->pos == TBL_DATA_DHORIZ)
251*4d131170SRobert Mustacchi 			print_otag(h, TAG_HR, "");
252*4d131170SRobert Mustacchi 		else if (dp->string != NULL) {
253*4d131170SRobert Mustacchi 			save_font = h->metac;
254*4d131170SRobert Mustacchi 			html_setfont(h, dp->layout->font);
255*4d131170SRobert Mustacchi 			if (dp->layout->pos == TBL_CELL_LONG)
256*4d131170SRobert Mustacchi 				print_text(h, "\\[u2003]");  /* em space */
257cec8643bSMichal Nowak 			print_text(h, dp->string);
258*4d131170SRobert Mustacchi 			if (dp->layout->pos == TBL_CELL_NUMBER) {
259*4d131170SRobert Mustacchi 				col = h->tbl.cols + dp->layout->col;
260*4d131170SRobert Mustacchi 				if (col->decimal < col->nwidth) {
261*4d131170SRobert Mustacchi 					if ((ccp = strrchr(dp->string,
262*4d131170SRobert Mustacchi 					    sp->opts->decimal)) == NULL) {
263*4d131170SRobert Mustacchi 						/* Punctuation space. */
264*4d131170SRobert Mustacchi 						print_text(h, "\\[u2008]");
265*4d131170SRobert Mustacchi 						ccp = strchr(dp->string, '\0');
266*4d131170SRobert Mustacchi 					} else
267*4d131170SRobert Mustacchi 						ccp++;
268*4d131170SRobert Mustacchi 					sz = col->nwidth - col->decimal;
269*4d131170SRobert Mustacchi 					while (--sz > 0) {
270*4d131170SRobert Mustacchi 						if (*ccp == '\0')
271*4d131170SRobert Mustacchi 							/* Figure space. */
272*4d131170SRobert Mustacchi 							print_text(h,
273*4d131170SRobert Mustacchi 							    "\\[u2007]");
274*4d131170SRobert Mustacchi 						else
275*4d131170SRobert Mustacchi 							ccp++;
276*4d131170SRobert Mustacchi 					}
277*4d131170SRobert Mustacchi 				}
278*4d131170SRobert Mustacchi 			}
279*4d131170SRobert Mustacchi 			html_setfont(h, save_font);
280*4d131170SRobert Mustacchi 		}
281cec8643bSMichal Nowak 	}
282cec8643bSMichal Nowak 
28395c635efSGarrett D'Amore 	print_tagq(h, tt);
28495c635efSGarrett D'Amore 
28595c635efSGarrett D'Amore 	h->flags &= ~HTML_NONOSPACE;
28695c635efSGarrett D'Amore 
287260e9a87SYuri Pankov 	if (sp->next == NULL) {
28895c635efSGarrett D'Amore 		assert(h->tbl.cols);
28995c635efSGarrett D'Amore 		free(h->tbl.cols);
29095c635efSGarrett D'Amore 		h->tbl.cols = NULL;
29195c635efSGarrett D'Amore 		print_tblclose(h);
29295c635efSGarrett D'Amore 	}
29395c635efSGarrett D'Amore }
294