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