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