aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/eqn_html.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2014-09-28 13:34:15 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2014-09-28 13:34:15 +0000
commitd23f651d6dcae3f8527b8a52ea288e5902ec9fe6 (patch)
tree6c8257d6a5265cb0feca1b83da10931543a23feb /eqn_html.c
parent9fbcf3aca2a19a98619def5baefd89f51ec207f0 (diff)
downloadmandoc-d23f651d6dcae3f8527b8a52ea288e5902ec9fe6.tar.gz
mandoc-d23f651d6dcae3f8527b8a52ea288e5902ec9fe6.tar.zst
mandoc-d23f651d6dcae3f8527b8a52ea288e5902ec9fe6.zip
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.c182
1 files changed, 155 insertions, 27 deletions
diff --git a/eqn_html.c b/eqn_html.c
index a5a904b7..fd77b919 100644
--- a/eqn_html.c
+++ b/eqn_html.c
@@ -1,4 +1,4 @@
-/* $Id: eqn_html.c,v 1.4 2014/08/10 23:54:41 schwarze Exp $ */
+/* $Id: eqn_html.c,v 1.5 2014/09/28 13:34:15 kristaps Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -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);
}