1 /* $Id: tbl_html.c,v 1.41 2022/04/23 14:02:17 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 static size_t html_tbl_sulen(const struct roffsu *, void *); 41 42 43 static size_t 44 html_tbl_len(size_t sz, void *arg) 45 { 46 return sz; 47 } 48 49 static size_t 50 html_tbl_strlen(const char *p, void *arg) 51 { 52 return strlen(p); 53 } 54 55 static size_t 56 html_tbl_sulen(const struct roffsu *su, void *arg) 57 { 58 if (su->scale < 0.0) 59 return 0; 60 61 switch (su->unit) { 62 case SCALE_FS: /* 2^16 basic units */ 63 return su->scale * 65536.0 / 24.0; 64 case SCALE_IN: /* 10 characters per inch */ 65 return su->scale * 10.0; 66 case SCALE_CM: /* 2.54 cm per inch */ 67 return su->scale * 10.0 / 2.54; 68 case SCALE_PC: /* 6 pica per inch */ 69 case SCALE_VS: 70 return su->scale * 10.0 / 6.0; 71 case SCALE_EN: 72 case SCALE_EM: 73 return su->scale; 74 case SCALE_PT: /* 12 points per pica */ 75 return su->scale * 10.0 / 6.0 / 12.0; 76 case SCALE_BU: /* 24 basic units per character */ 77 return su->scale / 24.0; 78 case SCALE_MM: /* 1/1000 inch */ 79 return su->scale / 100.0; 80 default: 81 abort(); 82 } 83 } 84 85 static void 86 html_tblopen(struct html *h, const struct tbl_span *sp) 87 { 88 html_close_paragraph(h); 89 if (h->tbl.cols == NULL) { 90 h->tbl.len = html_tbl_len; 91 h->tbl.slen = html_tbl_strlen; 92 h->tbl.sulen = html_tbl_sulen; 93 tblcalc(&h->tbl, sp, 0, 0); 94 } 95 assert(NULL == h->tblt); 96 h->tblt = print_otag(h, TAG_TABLE, "c?ss", "tbl", 97 "border", 98 sp->opts->opts & TBL_OPT_ALLBOX ? "1" : NULL, 99 "border-style", 100 sp->opts->opts & TBL_OPT_DBOX ? "double" : 101 sp->opts->opts & TBL_OPT_BOX ? "solid" : NULL, 102 "border-top-style", 103 sp->pos == TBL_SPAN_DHORIZ ? "double" : 104 sp->pos == TBL_SPAN_HORIZ ? "solid" : NULL); 105 } 106 107 void 108 print_tblclose(struct html *h) 109 { 110 111 assert(h->tblt); 112 print_tagq(h, h->tblt); 113 h->tblt = NULL; 114 } 115 116 void 117 print_tbl(struct html *h, const struct tbl_span *sp) 118 { 119 const struct tbl_dat *dp; 120 const struct tbl_cell *cp; 121 const struct tbl_span *psp; 122 const struct roffcol *col; 123 struct tag *tt; 124 const char *hspans, *vspans, *halign, *valign; 125 const char *bborder, *lborder, *rborder; 126 const char *ccp; 127 char hbuf[4], vbuf[4]; 128 size_t sz; 129 enum mandoc_esc save_font; 130 int i; 131 132 if (h->tblt == NULL) 133 html_tblopen(h, sp); 134 135 /* 136 * Horizontal lines spanning the whole table 137 * are handled by previous or following table rows. 138 */ 139 140 if (sp->pos != TBL_SPAN_DATA) 141 goto out; 142 143 /* Inhibit printing of spaces: we do padding ourselves. */ 144 145 h->flags |= HTML_NONOSPACE; 146 h->flags |= HTML_NOSPACE; 147 148 /* Draw a vertical line left of this row? */ 149 150 switch (sp->layout->vert) { 151 case 2: 152 lborder = "double"; 153 break; 154 case 1: 155 lborder = "solid"; 156 break; 157 default: 158 lborder = NULL; 159 break; 160 } 161 162 /* Draw a horizontal line below this row? */ 163 164 bborder = NULL; 165 if ((psp = sp->next) != NULL) { 166 switch (psp->pos) { 167 case TBL_SPAN_DHORIZ: 168 bborder = "double"; 169 break; 170 case TBL_SPAN_HORIZ: 171 bborder = "solid"; 172 break; 173 default: 174 break; 175 } 176 } 177 178 tt = print_otag(h, TAG_TR, "ss", 179 "border-left-style", lborder, 180 "border-bottom-style", bborder); 181 182 for (dp = sp->first; dp != NULL; dp = dp->next) { 183 print_stagq(h, tt); 184 185 /* 186 * Do not generate <td> elements for continuations 187 * of spanned cells. Larger <td> elements covering 188 * this space were already generated earlier. 189 */ 190 191 cp = dp->layout; 192 if (cp->pos == TBL_CELL_SPAN || cp->pos == TBL_CELL_DOWN || 193 (dp->string != NULL && strcmp(dp->string, "\\^") == 0)) 194 continue; 195 196 /* Determine the attribute values. */ 197 198 if (dp->hspans > 0) { 199 (void)snprintf(hbuf, sizeof(hbuf), 200 "%d", dp->hspans + 1); 201 hspans = hbuf; 202 } else 203 hspans = NULL; 204 if (dp->vspans > 0) { 205 (void)snprintf(vbuf, sizeof(vbuf), 206 "%d", dp->vspans + 1); 207 vspans = vbuf; 208 } else 209 vspans = NULL; 210 211 switch (cp->pos) { 212 case TBL_CELL_CENTRE: 213 halign = "center"; 214 break; 215 case TBL_CELL_RIGHT: 216 case TBL_CELL_NUMBER: 217 halign = "right"; 218 break; 219 default: 220 halign = NULL; 221 break; 222 } 223 if (cp->flags & TBL_CELL_TALIGN) 224 valign = "top"; 225 else if (cp->flags & TBL_CELL_BALIGN) 226 valign = "bottom"; 227 else 228 valign = NULL; 229 230 for (i = dp->hspans; i > 0; i--) 231 cp = cp->next; 232 switch (cp->vert) { 233 case 2: 234 rborder = "double"; 235 break; 236 case 1: 237 rborder = "solid"; 238 break; 239 default: 240 rborder = NULL; 241 break; 242 } 243 244 /* Print the element and the attributes. */ 245 246 print_otag(h, TAG_TD, "??sss", 247 "colspan", hspans, "rowspan", vspans, 248 "vertical-align", valign, 249 "text-align", halign, 250 "border-right-style", rborder); 251 if (dp->layout->pos == TBL_CELL_HORIZ || 252 dp->layout->pos == TBL_CELL_DHORIZ || 253 dp->pos == TBL_DATA_HORIZ || 254 dp->pos == TBL_DATA_NHORIZ || 255 dp->pos == TBL_DATA_DHORIZ || 256 dp->pos == TBL_DATA_NDHORIZ) 257 print_otag(h, TAG_HR, ""); 258 else if (dp->string != NULL) { 259 save_font = h->metac; 260 html_setfont(h, dp->layout->font); 261 if (dp->layout->pos == TBL_CELL_LONG) 262 print_text(h, "\\[u2003]"); /* em space */ 263 print_text(h, dp->string); 264 if (dp->layout->pos == TBL_CELL_NUMBER) { 265 col = h->tbl.cols + dp->layout->col; 266 if (col->decimal < col->nwidth) { 267 if ((ccp = strrchr(dp->string, 268 sp->opts->decimal)) == NULL) { 269 /* Punctuation space. */ 270 print_text(h, "\\[u2008]"); 271 ccp = strchr(dp->string, '\0'); 272 } else 273 ccp++; 274 sz = col->nwidth - col->decimal; 275 while (--sz > 0) { 276 if (*ccp == '\0') 277 /* Figure space. */ 278 print_text(h, 279 "\\[u2007]"); 280 else 281 ccp++; 282 } 283 } 284 } 285 html_setfont(h, save_font); 286 } 287 } 288 289 print_tagq(h, tt); 290 291 h->flags &= ~HTML_NONOSPACE; 292 293 out: 294 if (sp->next == NULL) { 295 assert(h->tbl.cols); 296 free(h->tbl.cols); 297 h->tbl.cols = NULL; 298 print_tblclose(h); 299 } 300 } 301