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