1*7295610fSBaptiste Daroussin /* $Id: eqn_html.c,v 1.18 2018/12/13 05:23:38 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 461d06d6bSBaptiste Daroussin * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 2161d06d6bSBaptiste Daroussin 2261d06d6bSBaptiste Daroussin #include <assert.h> 2361d06d6bSBaptiste Daroussin #include <ctype.h> 2461d06d6bSBaptiste Daroussin #include <stdio.h> 2561d06d6bSBaptiste Daroussin #include <stdlib.h> 2661d06d6bSBaptiste Daroussin #include <string.h> 2761d06d6bSBaptiste Daroussin 2861d06d6bSBaptiste Daroussin #include "mandoc.h" 29*7295610fSBaptiste Daroussin #include "eqn.h" 3061d06d6bSBaptiste Daroussin #include "out.h" 3161d06d6bSBaptiste Daroussin #include "html.h" 3261d06d6bSBaptiste Daroussin 3361d06d6bSBaptiste Daroussin static void 3461d06d6bSBaptiste Daroussin eqn_box(struct html *p, const struct eqn_box *bp) 3561d06d6bSBaptiste Daroussin { 3661d06d6bSBaptiste Daroussin struct tag *post, *row, *cell, *t; 3761d06d6bSBaptiste Daroussin const struct eqn_box *child, *parent; 3861d06d6bSBaptiste Daroussin const char *cp; 3961d06d6bSBaptiste Daroussin size_t i, j, rows; 4061d06d6bSBaptiste Daroussin enum htmltag tag; 4161d06d6bSBaptiste Daroussin enum eqn_fontt font; 4261d06d6bSBaptiste Daroussin 4361d06d6bSBaptiste Daroussin if (NULL == bp) 4461d06d6bSBaptiste Daroussin return; 4561d06d6bSBaptiste Daroussin 4661d06d6bSBaptiste Daroussin post = NULL; 4761d06d6bSBaptiste Daroussin 4861d06d6bSBaptiste Daroussin /* 4961d06d6bSBaptiste Daroussin * Special handling for a matrix, which is presented to us in 5061d06d6bSBaptiste Daroussin * column order, but must be printed in row-order. 5161d06d6bSBaptiste Daroussin */ 5261d06d6bSBaptiste Daroussin if (EQN_MATRIX == bp->type) { 5361d06d6bSBaptiste Daroussin if (NULL == bp->first) 5461d06d6bSBaptiste Daroussin goto out; 5561d06d6bSBaptiste Daroussin if (bp->first->type != EQN_LIST || 5661d06d6bSBaptiste Daroussin bp->first->expectargs == 1) { 5761d06d6bSBaptiste Daroussin eqn_box(p, bp->first); 5861d06d6bSBaptiste Daroussin goto out; 5961d06d6bSBaptiste Daroussin } 6061d06d6bSBaptiste Daroussin if (NULL == (parent = bp->first->first)) 6161d06d6bSBaptiste Daroussin goto out; 6261d06d6bSBaptiste Daroussin /* Estimate the number of rows, first. */ 6361d06d6bSBaptiste Daroussin if (NULL == (child = parent->first)) 6461d06d6bSBaptiste Daroussin goto out; 6561d06d6bSBaptiste Daroussin for (rows = 0; NULL != child; rows++) 6661d06d6bSBaptiste Daroussin child = child->next; 6761d06d6bSBaptiste Daroussin /* Print row-by-row. */ 6861d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MTABLE, ""); 6961d06d6bSBaptiste Daroussin for (i = 0; i < rows; i++) { 7061d06d6bSBaptiste Daroussin parent = bp->first->first; 7161d06d6bSBaptiste Daroussin row = print_otag(p, TAG_MTR, ""); 7261d06d6bSBaptiste Daroussin while (NULL != parent) { 7361d06d6bSBaptiste Daroussin child = parent->first; 7461d06d6bSBaptiste Daroussin for (j = 0; j < i; j++) { 7561d06d6bSBaptiste Daroussin if (NULL == child) 7661d06d6bSBaptiste Daroussin break; 7761d06d6bSBaptiste Daroussin child = child->next; 7861d06d6bSBaptiste Daroussin } 7961d06d6bSBaptiste Daroussin cell = print_otag(p, TAG_MTD, ""); 8061d06d6bSBaptiste Daroussin /* 8161d06d6bSBaptiste Daroussin * If we have no data for this 8261d06d6bSBaptiste Daroussin * particular cell, then print a 8361d06d6bSBaptiste Daroussin * placeholder and continue--don't puke. 8461d06d6bSBaptiste Daroussin */ 8561d06d6bSBaptiste Daroussin if (NULL != child) 8661d06d6bSBaptiste Daroussin eqn_box(p, child->first); 8761d06d6bSBaptiste Daroussin print_tagq(p, cell); 8861d06d6bSBaptiste Daroussin parent = parent->next; 8961d06d6bSBaptiste Daroussin } 9061d06d6bSBaptiste Daroussin print_tagq(p, row); 9161d06d6bSBaptiste Daroussin } 9261d06d6bSBaptiste Daroussin goto out; 9361d06d6bSBaptiste Daroussin } 9461d06d6bSBaptiste Daroussin 9561d06d6bSBaptiste Daroussin switch (bp->pos) { 9661d06d6bSBaptiste Daroussin case EQNPOS_TO: 9761d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MOVER, ""); 9861d06d6bSBaptiste Daroussin break; 9961d06d6bSBaptiste Daroussin case EQNPOS_SUP: 10061d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MSUP, ""); 10161d06d6bSBaptiste Daroussin break; 10261d06d6bSBaptiste Daroussin case EQNPOS_FROM: 10361d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MUNDER, ""); 10461d06d6bSBaptiste Daroussin break; 10561d06d6bSBaptiste Daroussin case EQNPOS_SUB: 10661d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MSUB, ""); 10761d06d6bSBaptiste Daroussin break; 10861d06d6bSBaptiste Daroussin case EQNPOS_OVER: 10961d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MFRAC, ""); 11061d06d6bSBaptiste Daroussin break; 11161d06d6bSBaptiste Daroussin case EQNPOS_FROMTO: 11261d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MUNDEROVER, ""); 11361d06d6bSBaptiste Daroussin break; 11461d06d6bSBaptiste Daroussin case EQNPOS_SUBSUP: 11561d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MSUBSUP, ""); 11661d06d6bSBaptiste Daroussin break; 11761d06d6bSBaptiste Daroussin case EQNPOS_SQRT: 11861d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MSQRT, ""); 11961d06d6bSBaptiste Daroussin break; 12061d06d6bSBaptiste Daroussin default: 12161d06d6bSBaptiste Daroussin break; 12261d06d6bSBaptiste Daroussin } 12361d06d6bSBaptiste Daroussin 12461d06d6bSBaptiste Daroussin if (bp->top || bp->bottom) { 12561d06d6bSBaptiste Daroussin assert(NULL == post); 12661d06d6bSBaptiste Daroussin if (bp->top && NULL == bp->bottom) 12761d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MOVER, ""); 12861d06d6bSBaptiste Daroussin else if (bp->top && bp->bottom) 12961d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MUNDEROVER, ""); 13061d06d6bSBaptiste Daroussin else if (bp->bottom) 13161d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MUNDER, ""); 13261d06d6bSBaptiste Daroussin } 13361d06d6bSBaptiste Daroussin 13461d06d6bSBaptiste Daroussin if (EQN_PILE == bp->type) { 13561d06d6bSBaptiste Daroussin assert(NULL == post); 13661d06d6bSBaptiste Daroussin if (bp->first != NULL && 13761d06d6bSBaptiste Daroussin bp->first->type == EQN_LIST && 13861d06d6bSBaptiste Daroussin bp->first->expectargs > 1) 13961d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MTABLE, ""); 14061d06d6bSBaptiste Daroussin } else if (bp->type == EQN_LIST && bp->expectargs > 1 && 14161d06d6bSBaptiste Daroussin bp->parent && bp->parent->type == EQN_PILE) { 14261d06d6bSBaptiste Daroussin assert(NULL == post); 14361d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MTR, ""); 14461d06d6bSBaptiste Daroussin print_otag(p, TAG_MTD, ""); 14561d06d6bSBaptiste Daroussin } 14661d06d6bSBaptiste Daroussin 14761d06d6bSBaptiste Daroussin if (bp->text != NULL) { 14861d06d6bSBaptiste Daroussin assert(post == NULL); 14961d06d6bSBaptiste Daroussin tag = TAG_MI; 15061d06d6bSBaptiste Daroussin cp = bp->text; 15161d06d6bSBaptiste Daroussin if (isdigit((unsigned char)cp[0]) || 15261d06d6bSBaptiste Daroussin (cp[0] == '.' && isdigit((unsigned char)cp[1]))) { 15361d06d6bSBaptiste Daroussin tag = TAG_MN; 15461d06d6bSBaptiste Daroussin while (*++cp != '\0') { 15561d06d6bSBaptiste Daroussin if (*cp != '.' && 15661d06d6bSBaptiste Daroussin isdigit((unsigned char)*cp) == 0) { 15761d06d6bSBaptiste Daroussin tag = TAG_MI; 15861d06d6bSBaptiste Daroussin break; 15961d06d6bSBaptiste Daroussin } 16061d06d6bSBaptiste Daroussin } 16161d06d6bSBaptiste Daroussin } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) { 16261d06d6bSBaptiste Daroussin tag = TAG_MO; 16361d06d6bSBaptiste Daroussin while (*cp != '\0') { 16461d06d6bSBaptiste Daroussin if (cp[0] == '\\' && cp[1] != '\0') { 16561d06d6bSBaptiste Daroussin cp++; 16661d06d6bSBaptiste Daroussin mandoc_escape(&cp, NULL, NULL); 16761d06d6bSBaptiste Daroussin } else if (isalnum((unsigned char)*cp)) { 16861d06d6bSBaptiste Daroussin tag = TAG_MI; 16961d06d6bSBaptiste Daroussin break; 17061d06d6bSBaptiste Daroussin } else 17161d06d6bSBaptiste Daroussin cp++; 17261d06d6bSBaptiste Daroussin } 17361d06d6bSBaptiste Daroussin } 17461d06d6bSBaptiste Daroussin font = bp->font; 17561d06d6bSBaptiste Daroussin if (bp->text[0] != '\0' && 17661d06d6bSBaptiste Daroussin (((tag == TAG_MN || tag == TAG_MO) && 17761d06d6bSBaptiste Daroussin font == EQNFONT_ROMAN) || 17861d06d6bSBaptiste Daroussin (tag == TAG_MI && font == (bp->text[1] == '\0' ? 17961d06d6bSBaptiste Daroussin EQNFONT_ITALIC : EQNFONT_ROMAN)))) 18061d06d6bSBaptiste Daroussin font = EQNFONT_NONE; 18161d06d6bSBaptiste Daroussin switch (font) { 18261d06d6bSBaptiste Daroussin case EQNFONT_NONE: 18361d06d6bSBaptiste Daroussin post = print_otag(p, tag, ""); 18461d06d6bSBaptiste Daroussin break; 18561d06d6bSBaptiste Daroussin case EQNFONT_ROMAN: 18661d06d6bSBaptiste Daroussin post = print_otag(p, tag, "?", "fontstyle", "normal"); 18761d06d6bSBaptiste Daroussin break; 18861d06d6bSBaptiste Daroussin case EQNFONT_BOLD: 18961d06d6bSBaptiste Daroussin case EQNFONT_FAT: 19061d06d6bSBaptiste Daroussin post = print_otag(p, tag, "?", "fontweight", "bold"); 19161d06d6bSBaptiste Daroussin break; 19261d06d6bSBaptiste Daroussin case EQNFONT_ITALIC: 19361d06d6bSBaptiste Daroussin post = print_otag(p, tag, "?", "fontstyle", "italic"); 19461d06d6bSBaptiste Daroussin break; 19561d06d6bSBaptiste Daroussin default: 19661d06d6bSBaptiste Daroussin abort(); 19761d06d6bSBaptiste Daroussin } 19861d06d6bSBaptiste Daroussin print_text(p, bp->text); 19961d06d6bSBaptiste Daroussin } else if (NULL == post) { 20061d06d6bSBaptiste Daroussin if (NULL != bp->left || NULL != bp->right) 20161d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MFENCED, "??", 20261d06d6bSBaptiste Daroussin "open", bp->left == NULL ? "" : bp->left, 20361d06d6bSBaptiste Daroussin "close", bp->right == NULL ? "" : bp->right); 20461d06d6bSBaptiste Daroussin if (NULL == post) 20561d06d6bSBaptiste Daroussin post = print_otag(p, TAG_MROW, ""); 20661d06d6bSBaptiste Daroussin else 20761d06d6bSBaptiste Daroussin print_otag(p, TAG_MROW, ""); 20861d06d6bSBaptiste Daroussin } 20961d06d6bSBaptiste Daroussin 21061d06d6bSBaptiste Daroussin eqn_box(p, bp->first); 21161d06d6bSBaptiste Daroussin 21261d06d6bSBaptiste Daroussin out: 21361d06d6bSBaptiste Daroussin if (NULL != bp->bottom) { 21461d06d6bSBaptiste Daroussin t = print_otag(p, TAG_MO, ""); 21561d06d6bSBaptiste Daroussin print_text(p, bp->bottom); 21661d06d6bSBaptiste Daroussin print_tagq(p, t); 21761d06d6bSBaptiste Daroussin } 21861d06d6bSBaptiste Daroussin if (NULL != bp->top) { 21961d06d6bSBaptiste Daroussin t = print_otag(p, TAG_MO, ""); 22061d06d6bSBaptiste Daroussin print_text(p, bp->top); 22161d06d6bSBaptiste Daroussin print_tagq(p, t); 22261d06d6bSBaptiste Daroussin } 22361d06d6bSBaptiste Daroussin 22461d06d6bSBaptiste Daroussin if (NULL != post) 22561d06d6bSBaptiste Daroussin print_tagq(p, post); 22661d06d6bSBaptiste Daroussin 22761d06d6bSBaptiste Daroussin eqn_box(p, bp->next); 22861d06d6bSBaptiste Daroussin } 22961d06d6bSBaptiste Daroussin 23061d06d6bSBaptiste Daroussin void 23161d06d6bSBaptiste Daroussin print_eqn(struct html *p, const struct eqn_box *bp) 23261d06d6bSBaptiste Daroussin { 23361d06d6bSBaptiste Daroussin struct tag *t; 23461d06d6bSBaptiste Daroussin 23561d06d6bSBaptiste Daroussin if (bp->first == NULL) 23661d06d6bSBaptiste Daroussin return; 23761d06d6bSBaptiste Daroussin 23861d06d6bSBaptiste Daroussin t = print_otag(p, TAG_MATH, "c", "eqn"); 23961d06d6bSBaptiste Daroussin 24061d06d6bSBaptiste Daroussin p->flags |= HTML_NONOSPACE; 24161d06d6bSBaptiste Daroussin eqn_box(p, bp); 24261d06d6bSBaptiste Daroussin p->flags &= ~HTML_NONOSPACE; 24361d06d6bSBaptiste Daroussin 24461d06d6bSBaptiste Daroussin print_tagq(p, t); 24561d06d6bSBaptiste Daroussin } 246