Annotation of src/usr.bin/mandoc/eqn_html.c, Revision 1.3
1.3 ! schwarze 1: /* $Id: eqn_html.c,v 1.2 2014/04/20 16:44:44 schwarze Exp $ */
1.1 schwarze 2: /*
1.3 ! schwarze 3: * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 schwarze 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 <assert.h>
18: #include <stdio.h>
19: #include <stdlib.h>
20: #include <string.h>
21:
22: #include "mandoc.h"
23: #include "out.h"
24: #include "html.h"
25:
26: static const enum htmltag fontmap[EQNFONT__MAX] = {
27: TAG_SPAN, /* EQNFONT_NONE */
28: TAG_SPAN, /* EQNFONT_ROMAN */
29: TAG_B, /* EQNFONT_BOLD */
30: TAG_B, /* EQNFONT_FAT */
31: TAG_I /* EQNFONT_ITALIC */
32: };
33:
1.3 ! schwarze 34: static const struct eqn_box *
! 35: eqn_box(struct html *, const struct eqn_box *, int);
1.1 schwarze 36:
37:
38: void
39: print_eqn(struct html *p, const struct eqn *ep)
40: {
41: struct htmlpair tag;
42: struct tag *t;
43:
44: PAIR_CLASS_INIT(&tag, "eqn");
1.3 ! schwarze 45: t = print_otag(p, TAG_MATH, 1, &tag);
1.1 schwarze 46:
47: p->flags |= HTML_NONOSPACE;
1.3 ! schwarze 48: eqn_box(p, ep->root, 1);
1.1 schwarze 49: p->flags &= ~HTML_NONOSPACE;
50:
51: print_tagq(p, t);
52: }
53:
1.3 ! schwarze 54: /*
! 55: * This function is fairly brittle.
! 56: * This is because the eqn syntax doesn't play so nicely with recusive
! 57: * formats, e.g.,
! 58: * foo sub bar sub baz
! 59: * ...needs to resolve into
! 60: * <msub> foo <msub> bar, baz </msub> </msub>
! 61: * In other words, we need to embed some recursive work.
! 62: * FIXME: this does NOT handle right-left associativity or precedence!
! 63: */
! 64: static const struct eqn_box *
! 65: eqn_box(struct html *p, const struct eqn_box *bp, int next)
1.1 schwarze 66: {
1.3 ! schwarze 67: struct tag *post, *pilet, *tmp;
! 68: struct htmlpair tag[2];
! 69: int skiptwo;
! 70:
! 71: if (NULL == bp)
! 72: return(NULL);
! 73:
! 74: post = pilet = NULL;
! 75: skiptwo = 0;
! 76:
! 77: /*
! 78: * If we're a "row" under a pile, then open up the piling
! 79: * context here.
! 80: * We do this first because the pile surrounds the content of
! 81: * the contained expression.
! 82: */
! 83: if (NULL != bp->parent && bp->parent->pile != EQNPILE_NONE) {
! 84: pilet = print_otag(p, TAG_MTR, 0, NULL);
! 85: print_otag(p, TAG_MTD, 0, NULL);
! 86: }
! 87:
! 88: /*
! 89: * If we're establishing a pile, start the table mode now.
! 90: * If we've already in a pile row, then don't override "pilet",
! 91: * because we'll be closed out anyway.
! 92: */
! 93: if (bp->pile != EQNPILE_NONE) {
! 94: tmp = print_otag(p, TAG_MTABLE, 0, NULL);
! 95: pilet = (NULL == pilet) ? tmp : pilet;
! 96: }
! 97:
! 98: /*
! 99: * Positioning.
! 100: * This is the most complicated part, and actually doesn't quite
! 101: * work (FIXME) because it doesn't account for associativity.
! 102: * Setting "post" will mean that we're only going to process a
! 103: * single or double following expression.
! 104: */
! 105: switch (bp->pos) {
! 106: case (EQNPOS_SUP):
! 107: post = print_otag(p, TAG_MSUP, 0, NULL);
! 108: break;
! 109: case (EQNPOS_FROM):
! 110: /* FALLTHROUGH */
! 111: case (EQNPOS_SUB):
! 112: post = print_otag(p, TAG_MSUB, 0, NULL);
! 113: break;
! 114: case (EQNPOS_OVER):
! 115: post = print_otag(p, TAG_MFRAC, 0, NULL);
! 116: break;
! 117: case (EQNPOS_SUBSUP):
! 118: /* This requires two elements. */
! 119: post = print_otag(p, TAG_MSUBSUP, 0, NULL);
! 120: skiptwo = 1;
! 121: break;
! 122: default:
! 123: break;
! 124: }
! 125:
! 126: /*t = EQNFONT_NONE == bp->font ? NULL :
! 127: print_otag(p, fontmap[(int)bp->font], 0, NULL);*/
! 128:
! 129: if (NULL != bp->text) {
! 130: assert(NULL == bp->first);
! 131: /*
! 132: * We have text.
! 133: * This can be a number, a function, a variable, or
! 134: * pretty much anything else.
! 135: * First, check for some known functions.
! 136: * If we're going to create a structural node (e.g.,
! 137: * sqrt), then set the "post" variable only if it's not
! 138: * already set.
! 139: */
! 140: if (0 == strcmp(bp->text, "sqrt")) {
! 141: tmp = print_otag(p, TAG_MSQRT, 0, NULL);
! 142: post = (NULL == post) ? tmp : post;
! 143: } else if (0 == strcmp(bp->text, "+") ||
! 144: 0 == strcmp(bp->text, "-") ||
! 145: 0 == strcmp(bp->text, "=") ||
! 146: 0 == strcmp(bp->text, "(") ||
! 147: 0 == strcmp(bp->text, ")") ||
! 148: 0 == strcmp(bp->text, "/")) {
! 149: tmp = print_otag(p, TAG_MO, 0, NULL);
! 150: print_text(p, bp->text);
! 151: print_tagq(p, tmp);
! 152: } else {
! 153: tmp = print_otag(p, TAG_MI, 0, NULL);
! 154: print_text(p, bp->text);
! 155: print_tagq(p, tmp);
! 156: }
! 157: } else if (NULL != bp->first) {
! 158: assert(NULL == bp->text);
! 159: /*
! 160: * If we're a "fenced" component (i.e., having
! 161: * brackets), then process those brackets now.
! 162: * Otherwise, introduce a dummy row (if we're not
! 163: * already in a table context).
! 164: */
! 165: tmp = NULL;
! 166: if (NULL != bp->left || NULL != bp->right) {
! 167: PAIR_INIT(&tag[0], ATTR_OPEN,
! 168: NULL != bp->left ? bp->left : "");
! 169: PAIR_INIT(&tag[1], ATTR_CLOSE,
! 170: NULL != bp->right ? bp->right : "");
! 171: tmp = print_otag(p, TAG_MFENCED, 2, tag);
! 172: print_otag(p, TAG_MROW, 0, NULL);
! 173: } else if (NULL == pilet)
! 174: tmp = print_otag(p, TAG_MROW, 0, NULL);
! 175: eqn_box(p, bp->first, 1);
! 176: if (NULL != tmp)
! 177: print_tagq(p, tmp);
! 178: }
! 179:
! 180: /*
! 181: * If a positional context, invoke the "next" context.
! 182: * This is recursive and will return the end of the recursive
! 183: * chain of "next" contexts.
! 184: */
! 185: if (NULL != post) {
! 186: bp = eqn_box(p, bp->next, 0);
! 187: if (skiptwo)
! 188: bp = eqn_box(p, bp->next, 0);
! 189: print_tagq(p, post);
! 190: }
! 191:
! 192: /*
! 193: * If we're being piled (either directly, in the table, or
! 194: * indirectly in a table row), then close that out.
! 195: */
! 196: if (NULL != pilet)
! 197: print_tagq(p, pilet);
! 198:
! 199: /*
! 200: * If we're normally processing, then grab the next node.
! 201: * If we're in a recursive context, then don't seek to the next
! 202: * node; further recursion has already been handled.
! 203: */
! 204: return(next ? eqn_box(p, bp->next, 1) : bp);
1.1 schwarze 205: }