1 /* $Id: eqn_html.c,v 1.19 2019/03/17 18:21:45 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include "config.h" 19 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "mandoc.h" 29 #include "roff.h" 30 #include "eqn.h" 31 #include "out.h" 32 #include "html.h" 33 34 static void 35 eqn_box(struct html *p, const struct eqn_box *bp) 36 { 37 struct tag *post, *row, *cell, *t; 38 const struct eqn_box *child, *parent; 39 const char *cp; 40 size_t i, j, rows; 41 enum htmltag tag; 42 enum eqn_fontt font; 43 44 if (NULL == bp) 45 return; 46 47 post = NULL; 48 49 /* 50 * Special handling for a matrix, which is presented to us in 51 * column order, but must be printed in row-order. 52 */ 53 if (EQN_MATRIX == bp->type) { 54 if (NULL == bp->first) 55 goto out; 56 if (bp->first->type != EQN_LIST || 57 bp->first->expectargs == 1) { 58 eqn_box(p, bp->first); 59 goto out; 60 } 61 if (NULL == (parent = bp->first->first)) 62 goto out; 63 /* Estimate the number of rows, first. */ 64 if (NULL == (child = parent->first)) 65 goto out; 66 for (rows = 0; NULL != child; rows++) 67 child = child->next; 68 /* Print row-by-row. */ 69 post = print_otag(p, TAG_MTABLE, ""); 70 for (i = 0; i < rows; i++) { 71 parent = bp->first->first; 72 row = print_otag(p, TAG_MTR, ""); 73 while (NULL != parent) { 74 child = parent->first; 75 for (j = 0; j < i; j++) { 76 if (NULL == child) 77 break; 78 child = child->next; 79 } 80 cell = print_otag(p, TAG_MTD, ""); 81 /* 82 * If we have no data for this 83 * particular cell, then print a 84 * placeholder and continue--don't puke. 85 */ 86 if (NULL != child) 87 eqn_box(p, child->first); 88 print_tagq(p, cell); 89 parent = parent->next; 90 } 91 print_tagq(p, row); 92 } 93 goto out; 94 } 95 96 switch (bp->pos) { 97 case EQNPOS_TO: 98 post = print_otag(p, TAG_MOVER, ""); 99 break; 100 case EQNPOS_SUP: 101 post = print_otag(p, TAG_MSUP, ""); 102 break; 103 case EQNPOS_FROM: 104 post = print_otag(p, TAG_MUNDER, ""); 105 break; 106 case EQNPOS_SUB: 107 post = print_otag(p, TAG_MSUB, ""); 108 break; 109 case EQNPOS_OVER: 110 post = print_otag(p, TAG_MFRAC, ""); 111 break; 112 case EQNPOS_FROMTO: 113 post = print_otag(p, TAG_MUNDEROVER, ""); 114 break; 115 case EQNPOS_SUBSUP: 116 post = print_otag(p, TAG_MSUBSUP, ""); 117 break; 118 case EQNPOS_SQRT: 119 post = print_otag(p, TAG_MSQRT, ""); 120 break; 121 default: 122 break; 123 } 124 125 if (bp->top || bp->bottom) { 126 assert(NULL == post); 127 if (bp->top && NULL == bp->bottom) 128 post = print_otag(p, TAG_MOVER, ""); 129 else if (bp->top && bp->bottom) 130 post = print_otag(p, TAG_MUNDEROVER, ""); 131 else if (bp->bottom) 132 post = print_otag(p, TAG_MUNDER, ""); 133 } 134 135 if (EQN_PILE == bp->type) { 136 assert(NULL == post); 137 if (bp->first != NULL && 138 bp->first->type == EQN_LIST && 139 bp->first->expectargs > 1) 140 post = print_otag(p, TAG_MTABLE, ""); 141 } else if (bp->type == EQN_LIST && bp->expectargs > 1 && 142 bp->parent && bp->parent->type == EQN_PILE) { 143 assert(NULL == post); 144 post = print_otag(p, TAG_MTR, ""); 145 print_otag(p, TAG_MTD, ""); 146 } 147 148 if (bp->text != NULL) { 149 assert(post == NULL); 150 tag = TAG_MI; 151 cp = bp->text; 152 if (isdigit((unsigned char)cp[0]) || 153 (cp[0] == '.' && isdigit((unsigned char)cp[1]))) { 154 tag = TAG_MN; 155 while (*++cp != '\0') { 156 if (*cp != '.' && 157 isdigit((unsigned char)*cp) == 0) { 158 tag = TAG_MI; 159 break; 160 } 161 } 162 } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) { 163 tag = TAG_MO; 164 while (*cp != '\0') { 165 if (cp[0] == '\\' && cp[1] != '\0') { 166 cp++; 167 mandoc_escape(&cp, NULL, NULL); 168 } else if (isalnum((unsigned char)*cp)) { 169 tag = TAG_MI; 170 break; 171 } else 172 cp++; 173 } 174 } 175 font = bp->font; 176 if (bp->text[0] != '\0' && 177 (((tag == TAG_MN || tag == TAG_MO) && 178 font == EQNFONT_ROMAN) || 179 (tag == TAG_MI && font == (bp->text[1] == '\0' ? 180 EQNFONT_ITALIC : EQNFONT_ROMAN)))) 181 font = EQNFONT_NONE; 182 switch (font) { 183 case EQNFONT_NONE: 184 post = print_otag(p, tag, ""); 185 break; 186 case EQNFONT_ROMAN: 187 post = print_otag(p, tag, "?", "fontstyle", "normal"); 188 break; 189 case EQNFONT_BOLD: 190 case EQNFONT_FAT: 191 post = print_otag(p, tag, "?", "fontweight", "bold"); 192 break; 193 case EQNFONT_ITALIC: 194 post = print_otag(p, tag, "?", "fontstyle", "italic"); 195 break; 196 default: 197 abort(); 198 } 199 print_text(p, bp->text); 200 } else if (NULL == post) { 201 if (NULL != bp->left || NULL != bp->right) 202 post = print_otag(p, TAG_MFENCED, "??", 203 "open", bp->left == NULL ? "" : bp->left, 204 "close", bp->right == NULL ? "" : bp->right); 205 if (NULL == post) 206 post = print_otag(p, TAG_MROW, ""); 207 else 208 print_otag(p, TAG_MROW, ""); 209 } 210 211 eqn_box(p, bp->first); 212 213 out: 214 if (NULL != bp->bottom) { 215 t = print_otag(p, TAG_MO, ""); 216 print_text(p, bp->bottom); 217 print_tagq(p, t); 218 } 219 if (NULL != bp->top) { 220 t = print_otag(p, TAG_MO, ""); 221 print_text(p, bp->top); 222 print_tagq(p, t); 223 } 224 225 if (NULL != post) 226 print_tagq(p, post); 227 228 eqn_box(p, bp->next); 229 } 230 231 void 232 print_eqn(struct html *p, const struct eqn_box *bp) 233 { 234 struct tag *t; 235 236 if (bp->first == NULL) 237 return; 238 239 t = print_otag(p, TAG_MATH, "c", "eqn"); 240 241 p->flags |= HTML_NONOSPACE; 242 eqn_box(p, bp); 243 p->flags &= ~HTML_NONOSPACE; 244 245 print_tagq(p, t); 246 } 247