1 /* $Id: eqn_html.c,v 1.17 2017/07/14 13:32:35 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 "out.h" 30 #include "html.h" 31 32 static void 33 eqn_box(struct html *p, const struct eqn_box *bp) 34 { 35 struct tag *post, *row, *cell, *t; 36 const struct eqn_box *child, *parent; 37 const char *cp; 38 size_t i, j, rows; 39 enum htmltag tag; 40 enum eqn_fontt font; 41 42 if (NULL == bp) 43 return; 44 45 post = NULL; 46 47 /* 48 * Special handling for a matrix, which is presented to us in 49 * column order, but must be printed in row-order. 50 */ 51 if (EQN_MATRIX == bp->type) { 52 if (NULL == bp->first) 53 goto out; 54 if (bp->first->type != EQN_LIST || 55 bp->first->expectargs == 1) { 56 eqn_box(p, bp->first); 57 goto out; 58 } 59 if (NULL == (parent = bp->first->first)) 60 goto out; 61 /* Estimate the number of rows, first. */ 62 if (NULL == (child = parent->first)) 63 goto out; 64 for (rows = 0; NULL != child; rows++) 65 child = child->next; 66 /* Print row-by-row. */ 67 post = print_otag(p, TAG_MTABLE, ""); 68 for (i = 0; i < rows; i++) { 69 parent = bp->first->first; 70 row = print_otag(p, TAG_MTR, ""); 71 while (NULL != parent) { 72 child = parent->first; 73 for (j = 0; j < i; j++) { 74 if (NULL == child) 75 break; 76 child = child->next; 77 } 78 cell = print_otag(p, TAG_MTD, ""); 79 /* 80 * If we have no data for this 81 * particular cell, then print a 82 * placeholder and continue--don't puke. 83 */ 84 if (NULL != child) 85 eqn_box(p, child->first); 86 print_tagq(p, cell); 87 parent = parent->next; 88 } 89 print_tagq(p, row); 90 } 91 goto out; 92 } 93 94 switch (bp->pos) { 95 case EQNPOS_TO: 96 post = print_otag(p, TAG_MOVER, ""); 97 break; 98 case EQNPOS_SUP: 99 post = print_otag(p, TAG_MSUP, ""); 100 break; 101 case EQNPOS_FROM: 102 post = print_otag(p, TAG_MUNDER, ""); 103 break; 104 case EQNPOS_SUB: 105 post = print_otag(p, TAG_MSUB, ""); 106 break; 107 case EQNPOS_OVER: 108 post = print_otag(p, TAG_MFRAC, ""); 109 break; 110 case EQNPOS_FROMTO: 111 post = print_otag(p, TAG_MUNDEROVER, ""); 112 break; 113 case EQNPOS_SUBSUP: 114 post = print_otag(p, TAG_MSUBSUP, ""); 115 break; 116 case EQNPOS_SQRT: 117 post = print_otag(p, TAG_MSQRT, ""); 118 break; 119 default: 120 break; 121 } 122 123 if (bp->top || bp->bottom) { 124 assert(NULL == post); 125 if (bp->top && NULL == bp->bottom) 126 post = print_otag(p, TAG_MOVER, ""); 127 else if (bp->top && bp->bottom) 128 post = print_otag(p, TAG_MUNDEROVER, ""); 129 else if (bp->bottom) 130 post = print_otag(p, TAG_MUNDER, ""); 131 } 132 133 if (EQN_PILE == bp->type) { 134 assert(NULL == post); 135 if (bp->first != NULL && 136 bp->first->type == EQN_LIST && 137 bp->first->expectargs > 1) 138 post = print_otag(p, TAG_MTABLE, ""); 139 } else if (bp->type == EQN_LIST && bp->expectargs > 1 && 140 bp->parent && bp->parent->type == EQN_PILE) { 141 assert(NULL == post); 142 post = print_otag(p, TAG_MTR, ""); 143 print_otag(p, TAG_MTD, ""); 144 } 145 146 if (bp->text != NULL) { 147 assert(post == NULL); 148 tag = TAG_MI; 149 cp = bp->text; 150 if (isdigit((unsigned char)cp[0]) || 151 (cp[0] == '.' && isdigit((unsigned char)cp[1]))) { 152 tag = TAG_MN; 153 while (*++cp != '\0') { 154 if (*cp != '.' && 155 isdigit((unsigned char)*cp) == 0) { 156 tag = TAG_MI; 157 break; 158 } 159 } 160 } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) { 161 tag = TAG_MO; 162 while (*cp != '\0') { 163 if (cp[0] == '\\' && cp[1] != '\0') { 164 cp++; 165 mandoc_escape(&cp, NULL, NULL); 166 } else if (isalnum((unsigned char)*cp)) { 167 tag = TAG_MI; 168 break; 169 } else 170 cp++; 171 } 172 } 173 font = bp->font; 174 if (bp->text[0] != '\0' && 175 (((tag == TAG_MN || tag == TAG_MO) && 176 font == EQNFONT_ROMAN) || 177 (tag == TAG_MI && font == (bp->text[1] == '\0' ? 178 EQNFONT_ITALIC : EQNFONT_ROMAN)))) 179 font = EQNFONT_NONE; 180 switch (font) { 181 case EQNFONT_NONE: 182 post = print_otag(p, tag, ""); 183 break; 184 case EQNFONT_ROMAN: 185 post = print_otag(p, tag, "?", "fontstyle", "normal"); 186 break; 187 case EQNFONT_BOLD: 188 case EQNFONT_FAT: 189 post = print_otag(p, tag, "?", "fontweight", "bold"); 190 break; 191 case EQNFONT_ITALIC: 192 post = print_otag(p, tag, "?", "fontstyle", "italic"); 193 break; 194 default: 195 abort(); 196 } 197 print_text(p, bp->text); 198 } else if (NULL == post) { 199 if (NULL != bp->left || NULL != bp->right) 200 post = print_otag(p, TAG_MFENCED, "??", 201 "open", bp->left == NULL ? "" : bp->left, 202 "close", bp->right == NULL ? "" : bp->right); 203 if (NULL == post) 204 post = print_otag(p, TAG_MROW, ""); 205 else 206 print_otag(p, TAG_MROW, ""); 207 } 208 209 eqn_box(p, bp->first); 210 211 out: 212 if (NULL != bp->bottom) { 213 t = print_otag(p, TAG_MO, ""); 214 print_text(p, bp->bottom); 215 print_tagq(p, t); 216 } 217 if (NULL != bp->top) { 218 t = print_otag(p, TAG_MO, ""); 219 print_text(p, bp->top); 220 print_tagq(p, t); 221 } 222 223 if (NULL != post) 224 print_tagq(p, post); 225 226 eqn_box(p, bp->next); 227 } 228 229 void 230 print_eqn(struct html *p, const struct eqn_box *bp) 231 { 232 struct tag *t; 233 234 if (bp->first == NULL) 235 return; 236 237 t = print_otag(p, TAG_MATH, "c", "eqn"); 238 239 p->flags |= HTML_NONOSPACE; 240 eqn_box(p, bp); 241 p->flags &= ~HTML_NONOSPACE; 242 243 print_tagq(p, t); 244 } 245