xref: /openbsd/usr.bin/mandoc/eqn_html.c (revision 8761b3c5)
1*8761b3c5Sschwarze /*	$OpenBSD: eqn_html.c,v 1.15 2019/03/17 18:20:07 schwarze Exp $ */
2f8618d99Sschwarze /*
3933d2e00Sschwarze  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4229cc7fdSschwarze  * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
5f8618d99Sschwarze  *
6f8618d99Sschwarze  * Permission to use, copy, modify, and distribute this software for any
7f8618d99Sschwarze  * purpose with or without fee is hereby granted, provided that the above
8f8618d99Sschwarze  * copyright notice and this permission notice appear in all copies.
9f8618d99Sschwarze  *
10f8618d99Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f8618d99Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f8618d99Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f8618d99Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f8618d99Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f8618d99Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f8618d99Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f8618d99Sschwarze  */
18f8617809Sschwarze #include <sys/types.h>
19f8617809Sschwarze 
20f8618d99Sschwarze #include <assert.h>
2172fef9e3Sschwarze #include <ctype.h>
22f8618d99Sschwarze #include <stdio.h>
23f8618d99Sschwarze #include <stdlib.h>
24f8618d99Sschwarze #include <string.h>
25f8618d99Sschwarze 
26f8618d99Sschwarze #include "mandoc.h"
27*8761b3c5Sschwarze #include "roff.h"
28d4c8d4a3Sschwarze #include "eqn.h"
29f8618d99Sschwarze #include "out.h"
30f8618d99Sschwarze #include "html.h"
31f8618d99Sschwarze 
32f8617809Sschwarze static void
eqn_box(struct html * p,const struct eqn_box * bp)33f8617809Sschwarze eqn_box(struct html *p, const struct eqn_box *bp)
34f8618d99Sschwarze {
35f8617809Sschwarze 	struct tag	*post, *row, *cell, *t;
36f8617809Sschwarze 	const struct eqn_box *child, *parent;
37005cddddSschwarze 	const char	*cp;
38f8617809Sschwarze 	size_t		 i, j, rows;
3972fef9e3Sschwarze 	enum htmltag	 tag;
4072fef9e3Sschwarze 	enum eqn_fontt	 font;
41f8618d99Sschwarze 
42933d2e00Sschwarze 	if (NULL == bp)
43f8617809Sschwarze 		return;
44f8618d99Sschwarze 
45f8617809Sschwarze 	post = NULL;
46f8618d99Sschwarze 
47933d2e00Sschwarze 	/*
48f8617809Sschwarze 	 * Special handling for a matrix, which is presented to us in
49f8617809Sschwarze 	 * column order, but must be printed in row-order.
50933d2e00Sschwarze 	 */
51f8617809Sschwarze 	if (EQN_MATRIX == bp->type) {
52f8617809Sschwarze 		if (NULL == bp->first)
53f8617809Sschwarze 			goto out;
5432e49996Sschwarze 		if (bp->first->type != EQN_LIST ||
5532e49996Sschwarze 		    bp->first->expectargs == 1) {
56a4dfe1bcSschwarze 			eqn_box(p, bp->first);
57a4dfe1bcSschwarze 			goto out;
58a4dfe1bcSschwarze 		}
59f8617809Sschwarze 		if (NULL == (parent = bp->first->first))
60f8617809Sschwarze 			goto out;
61f8617809Sschwarze 		/* Estimate the number of rows, first. */
62f8617809Sschwarze 		if (NULL == (child = parent->first))
63f8617809Sschwarze 			goto out;
64f8617809Sschwarze 		for (rows = 0; NULL != child; rows++)
65f8617809Sschwarze 			child = child->next;
66f8617809Sschwarze 		/* Print row-by-row. */
67229cc7fdSschwarze 		post = print_otag(p, TAG_MTABLE, "");
68f8617809Sschwarze 		for (i = 0; i < rows; i++) {
69f8617809Sschwarze 			parent = bp->first->first;
70229cc7fdSschwarze 			row = print_otag(p, TAG_MTR, "");
71f8617809Sschwarze 			while (NULL != parent) {
72f8617809Sschwarze 				child = parent->first;
73f8617809Sschwarze 				for (j = 0; j < i; j++) {
74f8617809Sschwarze 					if (NULL == child)
75f8617809Sschwarze 						break;
76f8617809Sschwarze 					child = child->next;
77933d2e00Sschwarze 				}
78229cc7fdSschwarze 				cell = print_otag(p, TAG_MTD, "");
79f8617809Sschwarze 				/*
80f8617809Sschwarze 				 * If we have no data for this
81f8617809Sschwarze 				 * particular cell, then print a
82f8617809Sschwarze 				 * placeholder and continue--don't puke.
83f8617809Sschwarze 				 */
84f8617809Sschwarze 				if (NULL != child)
85f8617809Sschwarze 					eqn_box(p, child->first);
86f8617809Sschwarze 				print_tagq(p, cell);
87f8617809Sschwarze 				parent = parent->next;
88f8617809Sschwarze 			}
89f8617809Sschwarze 			print_tagq(p, row);
90f8617809Sschwarze 		}
91f8617809Sschwarze 		goto out;
92a3108a0aSschwarze 	}
93933d2e00Sschwarze 
94933d2e00Sschwarze 	switch (bp->pos) {
95e74fa2aeSschwarze 	case EQNPOS_TO:
96229cc7fdSschwarze 		post = print_otag(p, TAG_MOVER, "");
97a3108a0aSschwarze 		break;
98e74fa2aeSschwarze 	case EQNPOS_SUP:
99229cc7fdSschwarze 		post = print_otag(p, TAG_MSUP, "");
100933d2e00Sschwarze 		break;
101e74fa2aeSschwarze 	case EQNPOS_FROM:
102229cc7fdSschwarze 		post = print_otag(p, TAG_MUNDER, "");
103a3108a0aSschwarze 		break;
104e74fa2aeSschwarze 	case EQNPOS_SUB:
105229cc7fdSschwarze 		post = print_otag(p, TAG_MSUB, "");
106933d2e00Sschwarze 		break;
107e74fa2aeSschwarze 	case EQNPOS_OVER:
108229cc7fdSschwarze 		post = print_otag(p, TAG_MFRAC, "");
109933d2e00Sschwarze 		break;
110e74fa2aeSschwarze 	case EQNPOS_FROMTO:
111229cc7fdSschwarze 		post = print_otag(p, TAG_MUNDEROVER, "");
112a3108a0aSschwarze 		break;
113e74fa2aeSschwarze 	case EQNPOS_SUBSUP:
114229cc7fdSschwarze 		post = print_otag(p, TAG_MSUBSUP, "");
115f8617809Sschwarze 		break;
116e74fa2aeSschwarze 	case EQNPOS_SQRT:
117229cc7fdSschwarze 		post = print_otag(p, TAG_MSQRT, "");
118933d2e00Sschwarze 		break;
119933d2e00Sschwarze 	default:
120933d2e00Sschwarze 		break;
121933d2e00Sschwarze 	}
122933d2e00Sschwarze 
123f8617809Sschwarze 	if (bp->top || bp->bottom) {
124f8617809Sschwarze 		assert(NULL == post);
125f8617809Sschwarze 		if (bp->top && NULL == bp->bottom)
126229cc7fdSschwarze 			post = print_otag(p, TAG_MOVER, "");
127f8617809Sschwarze 		else if (bp->top && bp->bottom)
128229cc7fdSschwarze 			post = print_otag(p, TAG_MUNDEROVER, "");
129f8617809Sschwarze 		else if (bp->bottom)
130229cc7fdSschwarze 			post = print_otag(p, TAG_MUNDER, "");
131f8617809Sschwarze 	}
132f8617809Sschwarze 
133f8617809Sschwarze 	if (EQN_PILE == bp->type) {
134f8617809Sschwarze 		assert(NULL == post);
13532e49996Sschwarze 		if (bp->first != NULL &&
13632e49996Sschwarze 		    bp->first->type == EQN_LIST &&
13732e49996Sschwarze 		    bp->first->expectargs > 1)
138229cc7fdSschwarze 			post = print_otag(p, TAG_MTABLE, "");
13932e49996Sschwarze 	} else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
140a4dfe1bcSschwarze 	    bp->parent && bp->parent->type == EQN_PILE) {
141f8617809Sschwarze 		assert(NULL == post);
142229cc7fdSschwarze 		post = print_otag(p, TAG_MTR, "");
143229cc7fdSschwarze 		print_otag(p, TAG_MTD, "");
144f8617809Sschwarze 	}
145933d2e00Sschwarze 
14672fef9e3Sschwarze 	if (bp->text != NULL) {
14772fef9e3Sschwarze 		assert(post == NULL);
14872fef9e3Sschwarze 		tag = TAG_MI;
149005cddddSschwarze 		cp = bp->text;
150005cddddSschwarze 		if (isdigit((unsigned char)cp[0]) ||
151005cddddSschwarze 		    (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
15272fef9e3Sschwarze 			tag = TAG_MN;
15372fef9e3Sschwarze 			while (*++cp != '\0') {
154005cddddSschwarze 				if (*cp != '.' &&
155005cddddSschwarze 				    isdigit((unsigned char)*cp) == 0) {
15672fef9e3Sschwarze 					tag = TAG_MI;
15772fef9e3Sschwarze 					break;
15872fef9e3Sschwarze 				}
15972fef9e3Sschwarze 			}
160005cddddSschwarze 		} else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
16172fef9e3Sschwarze 			tag = TAG_MO;
162005cddddSschwarze 			while (*cp != '\0') {
163005cddddSschwarze 				if (cp[0] == '\\' && cp[1] != '\0') {
164005cddddSschwarze 					cp++;
165005cddddSschwarze 					mandoc_escape(&cp, NULL, NULL);
166005cddddSschwarze 				} else if (isalnum((unsigned char)*cp)) {
16772fef9e3Sschwarze 					tag = TAG_MI;
16872fef9e3Sschwarze 					break;
169005cddddSschwarze 				} else
170005cddddSschwarze 					cp++;
17172fef9e3Sschwarze 			}
17272fef9e3Sschwarze 		}
17372fef9e3Sschwarze 		font = bp->font;
17472fef9e3Sschwarze 		if (bp->text[0] != '\0' &&
17572fef9e3Sschwarze 		    (((tag == TAG_MN || tag == TAG_MO) &&
17672fef9e3Sschwarze 		      font == EQNFONT_ROMAN) ||
17772fef9e3Sschwarze 		     (tag == TAG_MI && font == (bp->text[1] == '\0' ?
17872fef9e3Sschwarze 		      EQNFONT_ITALIC : EQNFONT_ROMAN))))
17972fef9e3Sschwarze 			font = EQNFONT_NONE;
18072fef9e3Sschwarze 		switch (font) {
18172fef9e3Sschwarze 		case EQNFONT_NONE:
18272fef9e3Sschwarze 			post = print_otag(p, tag, "");
18372fef9e3Sschwarze 			break;
18472fef9e3Sschwarze 		case EQNFONT_ROMAN:
18572fef9e3Sschwarze 			post = print_otag(p, tag, "?", "fontstyle", "normal");
18672fef9e3Sschwarze 			break;
18772fef9e3Sschwarze 		case EQNFONT_BOLD:
18872fef9e3Sschwarze 		case EQNFONT_FAT:
18972fef9e3Sschwarze 			post = print_otag(p, tag, "?", "fontweight", "bold");
19072fef9e3Sschwarze 			break;
19172fef9e3Sschwarze 		case EQNFONT_ITALIC:
19272fef9e3Sschwarze 			post = print_otag(p, tag, "?", "fontstyle", "italic");
19372fef9e3Sschwarze 			break;
19472fef9e3Sschwarze 		default:
19572fef9e3Sschwarze 			abort();
19672fef9e3Sschwarze 		}
197f8618d99Sschwarze 		print_text(p, bp->text);
198f8617809Sschwarze 	} else if (NULL == post) {
199229cc7fdSschwarze 		if (NULL != bp->left || NULL != bp->right)
200229cc7fdSschwarze 			post = print_otag(p, TAG_MFENCED, "??",
201229cc7fdSschwarze 			    "open", bp->left == NULL ? "" : bp->left,
202229cc7fdSschwarze 			    "close", bp->right == NULL ? "" : bp->right);
203f8617809Sschwarze 		if (NULL == post)
204229cc7fdSschwarze 			post = print_otag(p, TAG_MROW, "");
205f8617809Sschwarze 		else
206229cc7fdSschwarze 			print_otag(p, TAG_MROW, "");
207933d2e00Sschwarze 	}
208f8618d99Sschwarze 
209f8617809Sschwarze 	eqn_box(p, bp->first);
210f8617809Sschwarze 
211f8617809Sschwarze out:
212f8617809Sschwarze 	if (NULL != bp->bottom) {
213229cc7fdSschwarze 		t = print_otag(p, TAG_MO, "");
214f8617809Sschwarze 		print_text(p, bp->bottom);
215f8617809Sschwarze 		print_tagq(p, t);
216f8617809Sschwarze 	}
217f8617809Sschwarze 	if (NULL != bp->top) {
218229cc7fdSschwarze 		t = print_otag(p, TAG_MO, "");
219f8617809Sschwarze 		print_text(p, bp->top);
220f8617809Sschwarze 		print_tagq(p, t);
221f8617809Sschwarze 	}
222f8617809Sschwarze 
223f8617809Sschwarze 	if (NULL != post)
224933d2e00Sschwarze 		print_tagq(p, post);
225f8617809Sschwarze 
226f8617809Sschwarze 	eqn_box(p, bp->next);
227933d2e00Sschwarze }
228f8618d99Sschwarze 
229f8617809Sschwarze void
print_eqn(struct html * p,const struct eqn_box * bp)230bf9acac6Sschwarze print_eqn(struct html *p, const struct eqn_box *bp)
231f8617809Sschwarze {
232f8617809Sschwarze 	struct tag	*t;
233f8618d99Sschwarze 
2349884404aSschwarze 	if (bp->first == NULL)
2359884404aSschwarze 		return;
2369884404aSschwarze 
237229cc7fdSschwarze 	t = print_otag(p, TAG_MATH, "c", "eqn");
238f8617809Sschwarze 
239f8617809Sschwarze 	p->flags |= HTML_NONOSPACE;
240bf9acac6Sschwarze 	eqn_box(p, bp);
241f8617809Sschwarze 	p->flags &= ~HTML_NONOSPACE;
242f8617809Sschwarze 
243f8617809Sschwarze 	print_tagq(p, t);
244f8618d99Sschwarze }
245