xref: /freebsd-src/contrib/mandoc/eqn_html.c (revision 61d06d6bd19dafe8ea971dd43e8328fa1b473456)
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