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