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