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