1 /* $Id: tbl_html.c,v 1.42 2025/07/16 14:33:08 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2014, 2015, 2017, 2018, 2021, 2022 4 * Ingo Schwarze <schwarze@openbsd.org> 5 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include "config.h" 20 21 #include <sys/types.h> 22 23 #include <assert.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #if DEBUG_MEMORY 29 #include "mandoc_dbg.h" 30 #endif 31 #include "mandoc.h" 32 #include "roff.h" 33 #include "tbl.h" 34 #include "out.h" 35 #include "html.h" 36 37 static void html_tblopen(struct html *, const struct tbl_span *); 38 static size_t html_tbl_len(size_t, void *); 39 static size_t html_tbl_strlen(const char *, void *); 40 41 42 static size_t 43 html_tbl_len(size_t sz, void *arg) 44 { 45 return sz; 46 } 47 48 static size_t 49 html_tbl_strlen(const char *p, void *arg) 50 { 51 return strlen(p); 52 } 53 54 static void 55 html_tblopen(struct html *h, const struct tbl_span *sp) 56 { 57 html_close_paragraph(h); 58 if (h->tbl.cols == NULL) { 59 h->tbl.len = html_tbl_len; 60 h->tbl.slen = html_tbl_strlen; 61 tblcalc(&h->tbl, sp, 0, 0); 62 } 63 assert(NULL == h->tblt); 64 h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl", 65 "border", 66 sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL, 67 "border-style", 68 sp->opts->opts & TBL_OPT_DBOX ? "double" : 69 sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL, 70 "border-top-style", 71 sp->pos == TBL_SPAN_DHORIZ ? "double" : 72 sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL); 73 } 74 75 void 76 print_tblclose(struct html *h) 77 { 78 79 assert(h->tblt); 80 print_tagq(h, h->tblt); 81 h->tblt = NULL; 82 } 83 84 void 85 print_tbl(struct html *h, const struct tbl_span *sp) 86 { 87 const struct tbl_dat *dp; 88 const struct tbl_cell *cp; 89 const struct tbl_span *psp; 90 const struct roffcol *col; 91 struct tag *tt; 92 const char *hspans, *vspans, *halign, *valign; 93 const char *bborder, *lborder, *rborder; 94 const char *ccp; 95 char hbuf[4], vbuf[4]; 96 size_t sz; 97 enum mandoc_esc save_font; 98 int i; 99 100 if (h->tblt == NULL) 101 html_tblopen(h, sp); 102 103 /* 104 * Horizontal lines spanning the whole table 105 * are handled by previous or following table rows. 106 */ 107 108 if (sp->pos != TBL_SPAN_DATA) 109 goto out; 110 111 /* Inhibit printing of spaces: we do padding ourselves. */ 112 113 h->flags |= HTML_NONOSPACE; 114 h->flags |= HTML_NOSPACE; 115 116 /* Draw a vertical line left of this row? */ 117 118 switch (sp->layout->vert) { 119 case 2: 120 lborder = "double"; 121 break; 122 case 1: 123 lborder = "solid"; 124 break; 125 default: 126 lborder = NULL; 127 break; 128 } 129 130 /* Draw a horizontal line below this row? */ 131 132 bborder = NULL; 133 if ((psp = sp->next) != NULL) { 134 switch (psp->pos) { 135 case TBL_SPAN_DHORIZ: 136 bborder = "double"; 137 break; 138 case TBL_SPAN_HORIZ: 139 bborder = "solid"; 140 break; 141 default: 142 break; 143 } 144 } 145 146 tt = print_otag(h, TAG_TR, "ss", 147 "border-left-style", lborder, 148 "border-bottom-style", bborder); 149 150 for (dp = sp->first; dp != NULL; dp = dp->next) { 151 print_stagq(h, tt); 152 153 /* 154 * Do not generate <td> elements for continuations 155 * of spanned cells. Larger <td> elements covering 156 * this space were already generated earlier. 157 */ 158 159 cp = dp->layout; 160 if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN || 161 (dp->string != NULL && strcmp(dp->string, "\\^") == 0)) 162 continue; 163 164 /* Determine the attribute values. */ 165 166 if (dp->hspans > 0) { 167 (void)snprintf(hbuf, sizeof(hbuf), 168 "%d", dp->hspans + 1); 169 hspans = hbuf; 170 } else 171 hspans = NULL; 172 if (dp->vspans > 0) { 173 (void)snprintf(vbuf, sizeof(vbuf), 174 "%d", dp->vspans + 1); 175 vspans = vbuf; 176 } else 177 vspans = NULL; 178 179 switch (cp->pos) { 180 case TBL_CELL_CENTRE: 181 halign = "center"; 182 break; 183 case TBL_CELL_RIGHT: 184 case TBL_CELL_NUMBER: 185 halign = "right"; 186 break; 187 default: 188 halign = NULL; 189 break; 190 } 191 if (cp->flags & TBL_CELL_TALIGN) 192 valign = "top"; 193 else if (cp->flags & TBL_CELL_BALIGN) 194 valign = "bottom"; 195 else 196 valign = NULL; 197 198 for (i = dp->hspans; i > 0; i--) 199 cp = cp->next; 200 switch (cp->vert) { 201 case 2: 202 rborder = "double"; 203 break; 204 case 1: 205 rborder = "solid"; 206 break; 207 default: 208 rborder = NULL; 209 break; 210 } 211 212 /* Print the element and the attributes. */ 213 214 print_otag(h, TAG_TD, "??sss", 215 "colspan", hspans, "rowspan", vspans, 216 "vertical-align", valign, 217 "text-align", halign, 218 "border-right-style", rborder); 219 if (dp->layout->pos == TBL_CELL_HORIZ || 220 dp->layout->pos == TBL_CELL_DHORIZ || 221 dp->pos == TBL_DATA_HORIZ || 222 dp->pos == TBL_DATA_NHORIZ || 223 dp->pos == TBL_DATA_DHORIZ || 224 dp->pos == TBL_DATA_NDHORIZ) 225 print_otag(h, TAG_HR, ""); 226 else if (dp->string != NULL) { 227 save_font = h->metac; 228 html_setfont(h, dp->layout->font); 229 if (dp->layout->pos == TBL_CELL_LONG) 230 print_text(h, "\\[u2003]"); /* em space */ 231 print_text(h, dp->string); 232 if (dp->layout->pos == TBL_CELL_NUMBER) { 233 col = h->tbl.cols + dp->layout->col; 234 if (col->decimal < col->nwidth) { 235 if ((ccp = strrchr(dp->string, 236 sp->opts->decimal)) == NULL) { 237 /* Punctuation space. */ 238 print_text(h, "\\[u2008]"); 239 ccp = strchr(dp->string, '\0'); 240 } else 241 ccp++; 242 sz = col->nwidth - col->decimal; 243 while (--sz > 0) { 244 if (*ccp == '\0') 245 /* Figure space. */ 246 print_text(h, 247 "\\[u2007]"); 248 else 249 ccp++; 250 } 251 } 252 } 253 html_setfont(h, save_font); 254 } 255 } 256 257 print_tagq(h, tt); 258 259 h->flags &= ~HTML_NONOSPACE; 260 261 out: 262 if (sp->next == NULL) { 263 assert(h->tbl.cols); 264 free(h->tbl.cols); 265 h->tbl.cols = NULL; 266 print_tblclose(h); 267 } 268 } 269