xref: /netbsd-src/external/bsd/mdocml/dist/eqn_html.c (revision 544c191c349c1704c9d5e679d12ec15cff579663)
1*544c191cSchristos /*	Id: eqn_html.c,v 1.18 2018/12/13 05:23:38 schwarze Exp  */
2c5f73b34Sjoerg /*
3fec65c98Schristos  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
49508192eSchristos  * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
5c5f73b34Sjoerg  *
6c5f73b34Sjoerg  * Permission to use, copy, modify, and distribute this software for any
7c5f73b34Sjoerg  * purpose with or without fee is hereby granted, provided that the above
8c5f73b34Sjoerg  * copyright notice and this permission notice appear in all copies.
9c5f73b34Sjoerg  *
10c5f73b34Sjoerg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c5f73b34Sjoerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c5f73b34Sjoerg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c5f73b34Sjoerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c5f73b34Sjoerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c5f73b34Sjoerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c5f73b34Sjoerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c5f73b34Sjoerg  */
18c5f73b34Sjoerg #include "config.h"
19fec65c98Schristos 
20fec65c98Schristos #include <sys/types.h>
21c5f73b34Sjoerg 
22c5f73b34Sjoerg #include <assert.h>
23c9bcef03Schristos #include <ctype.h>
24c5f73b34Sjoerg #include <stdio.h>
25c5f73b34Sjoerg #include <stdlib.h>
26c5f73b34Sjoerg #include <string.h>
27c5f73b34Sjoerg 
28c5f73b34Sjoerg #include "mandoc.h"
29*544c191cSchristos #include "eqn.h"
30c5f73b34Sjoerg #include "out.h"
31c5f73b34Sjoerg #include "html.h"
32c5f73b34Sjoerg 
33fec65c98Schristos static void
eqn_box(struct html * p,const struct eqn_box * bp)34fec65c98Schristos eqn_box(struct html *p, const struct eqn_box *bp)
35fec65c98Schristos {
36fec65c98Schristos 	struct tag	*post, *row, *cell, *t;
37fec65c98Schristos 	const struct eqn_box *child, *parent;
38c9bcef03Schristos 	const char	*cp;
39fec65c98Schristos 	size_t		 i, j, rows;
40c9bcef03Schristos 	enum htmltag	 tag;
41c9bcef03Schristos 	enum eqn_fontt	 font;
42c5f73b34Sjoerg 
43fec65c98Schristos 	if (NULL == bp)
44fec65c98Schristos 		return;
45c5f73b34Sjoerg 
46fec65c98Schristos 	post = NULL;
47fec65c98Schristos 
48fec65c98Schristos 	/*
49fec65c98Schristos 	 * Special handling for a matrix, which is presented to us in
50fec65c98Schristos 	 * column order, but must be printed in row-order.
51fec65c98Schristos 	 */
52fec65c98Schristos 	if (EQN_MATRIX == bp->type) {
53fec65c98Schristos 		if (NULL == bp->first)
54fec65c98Schristos 			goto out;
55c9bcef03Schristos 		if (bp->first->type != EQN_LIST ||
56c9bcef03Schristos 		    bp->first->expectargs == 1) {
57fec65c98Schristos 			eqn_box(p, bp->first);
58fec65c98Schristos 			goto out;
59fec65c98Schristos 		}
60fec65c98Schristos 		if (NULL == (parent = bp->first->first))
61fec65c98Schristos 			goto out;
62fec65c98Schristos 		/* Estimate the number of rows, first. */
63fec65c98Schristos 		if (NULL == (child = parent->first))
64fec65c98Schristos 			goto out;
65fec65c98Schristos 		for (rows = 0; NULL != child; rows++)
66fec65c98Schristos 			child = child->next;
67fec65c98Schristos 		/* Print row-by-row. */
689508192eSchristos 		post = print_otag(p, TAG_MTABLE, "");
69fec65c98Schristos 		for (i = 0; i < rows; i++) {
70fec65c98Schristos 			parent = bp->first->first;
719508192eSchristos 			row = print_otag(p, TAG_MTR, "");
72fec65c98Schristos 			while (NULL != parent) {
73fec65c98Schristos 				child = parent->first;
74fec65c98Schristos 				for (j = 0; j < i; j++) {
75fec65c98Schristos 					if (NULL == child)
76fec65c98Schristos 						break;
77fec65c98Schristos 					child = child->next;
78fec65c98Schristos 				}
799508192eSchristos 				cell = print_otag(p, TAG_MTD, "");
80fec65c98Schristos 				/*
81fec65c98Schristos 				 * If we have no data for this
82fec65c98Schristos 				 * particular cell, then print a
83fec65c98Schristos 				 * placeholder and continue--don't puke.
84fec65c98Schristos 				 */
85fec65c98Schristos 				if (NULL != child)
86fec65c98Schristos 					eqn_box(p, child->first);
87fec65c98Schristos 				print_tagq(p, cell);
88fec65c98Schristos 				parent = parent->next;
89fec65c98Schristos 			}
90fec65c98Schristos 			print_tagq(p, row);
91fec65c98Schristos 		}
92fec65c98Schristos 		goto out;
93fec65c98Schristos 	}
94fec65c98Schristos 
95fec65c98Schristos 	switch (bp->pos) {
96c9bcef03Schristos 	case EQNPOS_TO:
979508192eSchristos 		post = print_otag(p, TAG_MOVER, "");
98fec65c98Schristos 		break;
99c9bcef03Schristos 	case EQNPOS_SUP:
1009508192eSchristos 		post = print_otag(p, TAG_MSUP, "");
101fec65c98Schristos 		break;
102c9bcef03Schristos 	case EQNPOS_FROM:
1039508192eSchristos 		post = print_otag(p, TAG_MUNDER, "");
104fec65c98Schristos 		break;
105c9bcef03Schristos 	case EQNPOS_SUB:
1069508192eSchristos 		post = print_otag(p, TAG_MSUB, "");
107fec65c98Schristos 		break;
108c9bcef03Schristos 	case EQNPOS_OVER:
1099508192eSchristos 		post = print_otag(p, TAG_MFRAC, "");
110fec65c98Schristos 		break;
111c9bcef03Schristos 	case EQNPOS_FROMTO:
1129508192eSchristos 		post = print_otag(p, TAG_MUNDEROVER, "");
113fec65c98Schristos 		break;
114c9bcef03Schristos 	case EQNPOS_SUBSUP:
1159508192eSchristos 		post = print_otag(p, TAG_MSUBSUP, "");
116fec65c98Schristos 		break;
117c9bcef03Schristos 	case EQNPOS_SQRT:
1189508192eSchristos 		post = print_otag(p, TAG_MSQRT, "");
119fec65c98Schristos 		break;
120fec65c98Schristos 	default:
121fec65c98Schristos 		break;
122fec65c98Schristos 	}
123fec65c98Schristos 
124fec65c98Schristos 	if (bp->top || bp->bottom) {
125fec65c98Schristos 		assert(NULL == post);
126fec65c98Schristos 		if (bp->top && NULL == bp->bottom)
1279508192eSchristos 			post = print_otag(p, TAG_MOVER, "");
128fec65c98Schristos 		else if (bp->top && bp->bottom)
1299508192eSchristos 			post = print_otag(p, TAG_MUNDEROVER, "");
130fec65c98Schristos 		else if (bp->bottom)
1319508192eSchristos 			post = print_otag(p, TAG_MUNDER, "");
132fec65c98Schristos 	}
133fec65c98Schristos 
134fec65c98Schristos 	if (EQN_PILE == bp->type) {
135fec65c98Schristos 		assert(NULL == post);
136c9bcef03Schristos 		if (bp->first != NULL &&
137c9bcef03Schristos 		    bp->first->type == EQN_LIST &&
138c9bcef03Schristos 		    bp->first->expectargs > 1)
1399508192eSchristos 			post = print_otag(p, TAG_MTABLE, "");
140c9bcef03Schristos 	} else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
141fec65c98Schristos 	    bp->parent && bp->parent->type == EQN_PILE) {
142fec65c98Schristos 		assert(NULL == post);
1439508192eSchristos 		post = print_otag(p, TAG_MTR, "");
1449508192eSchristos 		print_otag(p, TAG_MTD, "");
145fec65c98Schristos 	}
146fec65c98Schristos 
147c9bcef03Schristos 	if (bp->text != NULL) {
148c9bcef03Schristos 		assert(post == NULL);
149c9bcef03Schristos 		tag = TAG_MI;
150c9bcef03Schristos 		cp = bp->text;
151c9bcef03Schristos 		if (isdigit((unsigned char)cp[0]) ||
152c9bcef03Schristos 		    (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
153c9bcef03Schristos 			tag = TAG_MN;
154c9bcef03Schristos 			while (*++cp != '\0') {
155c9bcef03Schristos 				if (*cp != '.' &&
156c9bcef03Schristos 				    isdigit((unsigned char)*cp) == 0) {
157c9bcef03Schristos 					tag = TAG_MI;
158c9bcef03Schristos 					break;
159c9bcef03Schristos 				}
160c9bcef03Schristos 			}
161c9bcef03Schristos 		} else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
162c9bcef03Schristos 			tag = TAG_MO;
163c9bcef03Schristos 			while (*cp != '\0') {
164c9bcef03Schristos 				if (cp[0] == '\\' && cp[1] != '\0') {
165c9bcef03Schristos 					cp++;
166c9bcef03Schristos 					mandoc_escape(&cp, NULL, NULL);
167c9bcef03Schristos 				} else if (isalnum((unsigned char)*cp)) {
168c9bcef03Schristos 					tag = TAG_MI;
169c9bcef03Schristos 					break;
170c9bcef03Schristos 				} else
171c9bcef03Schristos 					cp++;
172c9bcef03Schristos 			}
173c9bcef03Schristos 		}
174c9bcef03Schristos 		font = bp->font;
175c9bcef03Schristos 		if (bp->text[0] != '\0' &&
176c9bcef03Schristos 		    (((tag == TAG_MN || tag == TAG_MO) &&
177c9bcef03Schristos 		      font == EQNFONT_ROMAN) ||
178c9bcef03Schristos 		     (tag == TAG_MI && font == (bp->text[1] == '\0' ?
179c9bcef03Schristos 		      EQNFONT_ITALIC : EQNFONT_ROMAN))))
180c9bcef03Schristos 			font = EQNFONT_NONE;
181c9bcef03Schristos 		switch (font) {
182c9bcef03Schristos 		case EQNFONT_NONE:
183c9bcef03Schristos 			post = print_otag(p, tag, "");
184c9bcef03Schristos 			break;
185c9bcef03Schristos 		case EQNFONT_ROMAN:
186c9bcef03Schristos 			post = print_otag(p, tag, "?", "fontstyle", "normal");
187c9bcef03Schristos 			break;
188c9bcef03Schristos 		case EQNFONT_BOLD:
189c9bcef03Schristos 		case EQNFONT_FAT:
190c9bcef03Schristos 			post = print_otag(p, tag, "?", "fontweight", "bold");
191c9bcef03Schristos 			break;
192c9bcef03Schristos 		case EQNFONT_ITALIC:
193c9bcef03Schristos 			post = print_otag(p, tag, "?", "fontstyle", "italic");
194c9bcef03Schristos 			break;
195c9bcef03Schristos 		default:
196c9bcef03Schristos 			abort();
197c9bcef03Schristos 		}
198fec65c98Schristos 		print_text(p, bp->text);
199fec65c98Schristos 	} else if (NULL == post) {
2009508192eSchristos 		if (NULL != bp->left || NULL != bp->right)
2019508192eSchristos 			post = print_otag(p, TAG_MFENCED, "??",
2029508192eSchristos 			    "open", bp->left == NULL ? "" : bp->left,
2039508192eSchristos 			    "close", bp->right == NULL ? "" : bp->right);
204fec65c98Schristos 		if (NULL == post)
2059508192eSchristos 			post = print_otag(p, TAG_MROW, "");
206fec65c98Schristos 		else
2079508192eSchristos 			print_otag(p, TAG_MROW, "");
208fec65c98Schristos 	}
209fec65c98Schristos 
210fec65c98Schristos 	eqn_box(p, bp->first);
211fec65c98Schristos 
212fec65c98Schristos out:
213fec65c98Schristos 	if (NULL != bp->bottom) {
2149508192eSchristos 		t = print_otag(p, TAG_MO, "");
215fec65c98Schristos 		print_text(p, bp->bottom);
216fec65c98Schristos 		print_tagq(p, t);
217fec65c98Schristos 	}
218fec65c98Schristos 	if (NULL != bp->top) {
2199508192eSchristos 		t = print_otag(p, TAG_MO, "");
220fec65c98Schristos 		print_text(p, bp->top);
221fec65c98Schristos 		print_tagq(p, t);
222fec65c98Schristos 	}
223fec65c98Schristos 
224fec65c98Schristos 	if (NULL != post)
225fec65c98Schristos 		print_tagq(p, post);
226fec65c98Schristos 
227fec65c98Schristos 	eqn_box(p, bp->next);
228fec65c98Schristos }
229c5f73b34Sjoerg 
230c5f73b34Sjoerg void
print_eqn(struct html * p,const struct eqn_box * bp)231c9bcef03Schristos print_eqn(struct html *p, const struct eqn_box *bp)
232c5f73b34Sjoerg {
233c5f73b34Sjoerg 	struct tag	*t;
234c5f73b34Sjoerg 
235c9bcef03Schristos 	if (bp->first == NULL)
236c9bcef03Schristos 		return;
237c9bcef03Schristos 
2389508192eSchristos 	t = print_otag(p, TAG_MATH, "c", "eqn");
239c5f73b34Sjoerg 
240c5f73b34Sjoerg 	p->flags |= HTML_NONOSPACE;
241c9bcef03Schristos 	eqn_box(p, bp);
242c5f73b34Sjoerg 	p->flags &= ~HTML_NONOSPACE;
243c5f73b34Sjoerg 
244c5f73b34Sjoerg 	print_tagq(p, t);
245c5f73b34Sjoerg }
246