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