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