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