diff options
author | Kristaps Dzonsons <kristaps@bsd.lv> | 2014-09-28 13:34:15 +0000 |
---|---|---|
committer | Kristaps Dzonsons <kristaps@bsd.lv> | 2014-09-28 13:34:15 +0000 |
commit | e043d8c809b13f0720f27dcca52588d79f954ac0 (patch) | |
tree | 30b0282e13387baad2b6a97ca0234f0aa7a3132c /eqn_html.c | |
parent | ffb8ed4f9bbbf465632bd651b71377fa7d058dd3 (diff) | |
download | mandoc-e043d8c809b13f0720f27dcca52588d79f954ac0.tar.gz |
Support a decent subset of eqn(7) in MathML.
This has basic support for positions (under, sup, sub, sub/sup) and piles.
It *does not* support right-left grouping (among many other things), e.g.,
a sub b over c sub d
Which it will interpret, for the time being, as
a sub { b over { c sub d } }
instead of
{ a sub b } over { c sub d }
However, left-right grouping works fine.
Diffstat (limited to 'eqn_html.c')
-rw-r--r-- | eqn_html.c | 180 |
1 files changed, 154 insertions, 26 deletions
@@ -35,7 +35,8 @@ static const enum htmltag fontmap[EQNFONT__MAX] = { TAG_I /* EQNFONT_ITALIC */ }; -static void eqn_box(struct html *, const struct eqn_box *); +static const struct eqn_box * + eqn_box(struct html *, const struct eqn_box *, int); void @@ -45,37 +46,164 @@ print_eqn(struct html *p, const struct eqn *ep) struct tag *t; PAIR_CLASS_INIT(&tag, "eqn"); - t = print_otag(p, TAG_SPAN, 1, &tag); + t = print_otag(p, TAG_MATH, 1, &tag); p->flags |= HTML_NONOSPACE; - eqn_box(p, ep->root); + eqn_box(p, ep->root, 1); p->flags &= ~HTML_NONOSPACE; print_tagq(p, t); } -static void -eqn_box(struct html *p, const struct eqn_box *bp) +/* + * This function is fairly brittle. + * This is because the eqn syntax doesn't play so nicely with recusive + * formats, e.g., + * foo sub bar sub baz + * ...needs to resolve into + * <msub> foo <msub> bar, baz </msub> </msub> + * In other words, we need to embed some recursive work. + * FIXME: this does NOT handle right-left associativity or precedence! + */ +static const struct eqn_box * +eqn_box(struct html *p, const struct eqn_box *bp, int next) { - struct tag *t; - - t = EQNFONT_NONE == bp->font ? NULL : - print_otag(p, fontmap[(int)bp->font], 0, NULL); - - if (bp->left) - print_text(p, bp->left); - - if (bp->text) - print_text(p, bp->text); - - if (bp->first) - eqn_box(p, bp->first); - - if (NULL != t) - print_tagq(p, t); - if (bp->right) - print_text(p, bp->right); - - if (bp->next) - eqn_box(p, bp->next); + struct tag *post, *pilet, *tmp; + struct htmlpair tag[2]; + int skiptwo; + + if (NULL == bp) + return(NULL); + + post = pilet = NULL; + skiptwo = 0; + + /* + * If we're a "row" under a pile, then open up the piling + * context here. + * We do this first because the pile surrounds the content of + * the contained expression. + */ + if (NULL != bp->parent && bp->parent->pile != EQNPILE_NONE) { + pilet = print_otag(p, TAG_MTR, 0, NULL); + print_otag(p, TAG_MTD, 0, NULL); + } + + /* + * If we're establishing a pile, start the table mode now. + * If we've already in a pile row, then don't override "pilet", + * because we'll be closed out anyway. + */ + if (bp->pile != EQNPILE_NONE) { + tmp = print_otag(p, TAG_MTABLE, 0, NULL); + pilet = (NULL == pilet) ? tmp : pilet; + } + + /* + * Positioning. + * This is the most complicated part, and actually doesn't quite + * work (FIXME) because it doesn't account for associativity. + * Setting "post" will mean that we're only going to process a + * single or double following expression. + */ + switch (bp->pos) { + case (EQNPOS_SUP): + post = print_otag(p, TAG_MSUP, 0, NULL); + break; + case (EQNPOS_FROM): + /* FALLTHROUGH */ + case (EQNPOS_SUB): + post = print_otag(p, TAG_MSUB, 0, NULL); + break; + case (EQNPOS_OVER): + post = print_otag(p, TAG_MFRAC, 0, NULL); + break; + case (EQNPOS_SUBSUP): + /* This requires two elements. */ + post = print_otag(p, TAG_MSUBSUP, 0, NULL); + skiptwo = 1; + break; + default: + break; + } + + /*t = EQNFONT_NONE == bp->font ? NULL : + print_otag(p, fontmap[(int)bp->font], 0, NULL);*/ + + if (NULL != bp->text) { + assert(NULL == bp->first); + /* + * We have text. + * This can be a number, a function, a variable, or + * pretty much anything else. + * First, check for some known functions. + * If we're going to create a structural node (e.g., + * sqrt), then set the "post" variable only if it's not + * already set. + */ + if (0 == strcmp(bp->text, "sqrt")) { + tmp = print_otag(p, TAG_MSQRT, 0, NULL); + post = (NULL == post) ? tmp : post; + } else if (0 == strcmp(bp->text, "+") || + 0 == strcmp(bp->text, "-") || + 0 == strcmp(bp->text, "=") || + 0 == strcmp(bp->text, "(") || + 0 == strcmp(bp->text, ")") || + 0 == strcmp(bp->text, "/")) { + tmp = print_otag(p, TAG_MO, 0, NULL); + print_text(p, bp->text); + print_tagq(p, tmp); + } else { + tmp = print_otag(p, TAG_MI, 0, NULL); + print_text(p, bp->text); + print_tagq(p, tmp); + } + } else if (NULL != bp->first) { + assert(NULL == bp->text); + /* + * If we're a "fenced" component (i.e., having + * brackets), then process those brackets now. + * Otherwise, introduce a dummy row (if we're not + * already in a table context). + */ + tmp = NULL; + if (NULL != bp->left || NULL != bp->right) { + PAIR_INIT(&tag[0], ATTR_OPEN, + NULL != bp->left ? bp->left : ""); + PAIR_INIT(&tag[1], ATTR_CLOSE, + NULL != bp->right ? bp->right : ""); + tmp = print_otag(p, TAG_MFENCED, 2, tag); + print_otag(p, TAG_MROW, 0, NULL); + } else if (NULL == pilet) + tmp = print_otag(p, TAG_MROW, 0, NULL); + eqn_box(p, bp->first, 1); + if (NULL != tmp) + print_tagq(p, tmp); + } + + /* + * If a positional context, invoke the "next" context. + * This is recursive and will return the end of the recursive + * chain of "next" contexts. + */ + if (NULL != post) { + bp = eqn_box(p, bp->next, 0); + if (skiptwo) + bp = eqn_box(p, bp->next, 0); + print_tagq(p, post); + } + + /* + * If we're being piled (either directly, in the table, or + * indirectly in a table row), then close that out. + */ + if (NULL != pilet) + print_tagq(p, pilet); + + /* + * If we're normally processing, then grab the next node. + * If we're in a recursive context, then don't seek to the next + * node; further recursion has already been handled. + */ + return(next ? eqn_box(p, bp->next, 1) : bp); } |