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