Annotation of mandoc/eqn_html.c, Revision 1.7
1.7 ! kristaps 1: /* $Id: eqn_html.c,v 1.6 2014/09/28 14:06:09 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include "config.h"
1.4 schwarze 18:
19: #include <sys/types.h>
1.1 kristaps 20:
21: #include <assert.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25:
26: #include "mandoc.h"
27: #include "out.h"
28: #include "html.h"
29:
1.2 kristaps 30: static const enum htmltag fontmap[EQNFONT__MAX] = {
31: TAG_SPAN, /* EQNFONT_NONE */
32: TAG_SPAN, /* EQNFONT_ROMAN */
33: TAG_B, /* EQNFONT_BOLD */
34: TAG_B, /* EQNFONT_FAT */
35: TAG_I /* EQNFONT_ITALIC */
36: };
37:
1.5 kristaps 38: static const struct eqn_box *
39: eqn_box(struct html *, const struct eqn_box *, int);
1.2 kristaps 40:
1.1 kristaps 41:
42: void
43: print_eqn(struct html *p, const struct eqn *ep)
44: {
45: struct htmlpair tag;
46: struct tag *t;
47:
48: PAIR_CLASS_INIT(&tag, "eqn");
1.5 kristaps 49: t = print_otag(p, TAG_MATH, 1, &tag);
1.1 kristaps 50:
51: p->flags |= HTML_NONOSPACE;
1.5 kristaps 52: eqn_box(p, ep->root, 1);
1.1 kristaps 53: p->flags &= ~HTML_NONOSPACE;
54:
55: print_tagq(p, t);
56: }
57:
1.5 kristaps 58: /*
59: * This function is fairly brittle.
60: * This is because the eqn syntax doesn't play so nicely with recusive
61: * formats, e.g.,
62: * foo sub bar sub baz
63: * ...needs to resolve into
64: * <msub> foo <msub> bar, baz </msub> </msub>
65: * In other words, we need to embed some recursive work.
66: * FIXME: this does NOT handle right-left associativity or precedence!
67: */
68: static const struct eqn_box *
69: eqn_box(struct html *p, const struct eqn_box *bp, int next)
1.1 kristaps 70: {
1.5 kristaps 71: struct tag *post, *pilet, *tmp;
72: struct htmlpair tag[2];
73: int skiptwo;
74:
75: if (NULL == bp)
76: return(NULL);
77:
78: post = pilet = NULL;
79: skiptwo = 0;
80:
81: /*
82: * If we're a "row" under a pile, then open up the piling
83: * context here.
84: * We do this first because the pile surrounds the content of
85: * the contained expression.
86: */
87: if (NULL != bp->parent && bp->parent->pile != EQNPILE_NONE) {
88: pilet = print_otag(p, TAG_MTR, 0, NULL);
89: print_otag(p, TAG_MTD, 0, NULL);
90: }
1.6 kristaps 91: if (NULL != bp->parent && bp->parent->type == EQN_MATRIX) {
92: pilet = print_otag(p, TAG_MTABLE, 0, NULL);
93: print_otag(p, TAG_MTR, 0, NULL);
94: print_otag(p, TAG_MTD, 0, NULL);
95: }
1.5 kristaps 96:
97: /*
98: * If we're establishing a pile, start the table mode now.
99: * If we've already in a pile row, then don't override "pilet",
100: * because we'll be closed out anyway.
101: */
102: if (bp->pile != EQNPILE_NONE) {
103: tmp = print_otag(p, TAG_MTABLE, 0, NULL);
104: pilet = (NULL == pilet) ? tmp : pilet;
105: }
106:
107: /*
108: * Positioning.
109: * This is the most complicated part, and actually doesn't quite
110: * work (FIXME) because it doesn't account for associativity.
111: * Setting "post" will mean that we're only going to process a
112: * single or double following expression.
113: */
114: switch (bp->pos) {
1.6 kristaps 115: case (EQNPOS_TO):
1.7 ! kristaps 116: post = print_otag(p, TAG_MOVER, 0, NULL);
! 117: break;
1.5 kristaps 118: case (EQNPOS_SUP):
119: post = print_otag(p, TAG_MSUP, 0, NULL);
120: break;
121: case (EQNPOS_FROM):
1.7 ! kristaps 122: post = print_otag(p, TAG_MUNDER, 0, NULL);
! 123: break;
1.5 kristaps 124: case (EQNPOS_SUB):
125: post = print_otag(p, TAG_MSUB, 0, NULL);
126: break;
127: case (EQNPOS_OVER):
128: post = print_otag(p, TAG_MFRAC, 0, NULL);
129: break;
1.6 kristaps 130: case (EQNPOS_FROMTO):
1.7 ! kristaps 131: post = print_otag(p, TAG_MUNDEROVER, 0, NULL);
! 132: skiptwo = 1;
! 133: break;
1.5 kristaps 134: case (EQNPOS_SUBSUP):
135: post = print_otag(p, TAG_MSUBSUP, 0, NULL);
136: skiptwo = 1;
137: break;
138: default:
139: break;
140: }
141:
142: /*t = EQNFONT_NONE == bp->font ? NULL :
143: print_otag(p, fontmap[(int)bp->font], 0, NULL);*/
144:
145: if (NULL != bp->text) {
146: assert(NULL == bp->first);
147: /*
148: * We have text.
149: * This can be a number, a function, a variable, or
150: * pretty much anything else.
151: * First, check for some known functions.
152: * If we're going to create a structural node (e.g.,
153: * sqrt), then set the "post" variable only if it's not
154: * already set.
155: */
156: if (0 == strcmp(bp->text, "sqrt")) {
157: tmp = print_otag(p, TAG_MSQRT, 0, NULL);
158: post = (NULL == post) ? tmp : post;
159: } else if (0 == strcmp(bp->text, "+") ||
160: 0 == strcmp(bp->text, "-") ||
161: 0 == strcmp(bp->text, "=") ||
162: 0 == strcmp(bp->text, "(") ||
163: 0 == strcmp(bp->text, ")") ||
164: 0 == strcmp(bp->text, "/")) {
165: tmp = print_otag(p, TAG_MO, 0, NULL);
166: print_text(p, bp->text);
167: print_tagq(p, tmp);
168: } else {
169: tmp = print_otag(p, TAG_MI, 0, NULL);
170: print_text(p, bp->text);
171: print_tagq(p, tmp);
172: }
173: } else if (NULL != bp->first) {
174: assert(NULL == bp->text);
175: /*
176: * If we're a "fenced" component (i.e., having
177: * brackets), then process those brackets now.
178: * Otherwise, introduce a dummy row (if we're not
179: * already in a table context).
180: */
181: tmp = NULL;
182: if (NULL != bp->left || NULL != bp->right) {
183: PAIR_INIT(&tag[0], ATTR_OPEN,
184: NULL != bp->left ? bp->left : "");
185: PAIR_INIT(&tag[1], ATTR_CLOSE,
186: NULL != bp->right ? bp->right : "");
187: tmp = print_otag(p, TAG_MFENCED, 2, tag);
188: print_otag(p, TAG_MROW, 0, NULL);
189: } else if (NULL == pilet)
190: tmp = print_otag(p, TAG_MROW, 0, NULL);
191: eqn_box(p, bp->first, 1);
192: if (NULL != tmp)
193: print_tagq(p, tmp);
194: }
195:
196: /*
197: * If a positional context, invoke the "next" context.
198: * This is recursive and will return the end of the recursive
199: * chain of "next" contexts.
200: */
201: if (NULL != post) {
202: bp = eqn_box(p, bp->next, 0);
203: if (skiptwo)
204: bp = eqn_box(p, bp->next, 0);
205: print_tagq(p, post);
206: }
207:
208: /*
209: * If we're being piled (either directly, in the table, or
210: * indirectly in a table row), then close that out.
211: */
212: if (NULL != pilet)
213: print_tagq(p, pilet);
214:
215: /*
216: * If we're normally processing, then grab the next node.
217: * If we're in a recursive context, then don't seek to the next
218: * node; further recursion has already been handled.
219: */
220: return(next ? eqn_box(p, bp->next, 1) : bp);
1.1 kristaps 221: }
CVSweb