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