/* $Id: eqn_html.c,v 1.5 2014/09/28 13:34:15 kristaps Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include "mandoc.h"
#include "out.h"
#include "html.h"
static const enum htmltag fontmap[EQNFONT__MAX] = {
TAG_SPAN, /* EQNFONT_NONE */
TAG_SPAN, /* EQNFONT_ROMAN */
TAG_B, /* EQNFONT_BOLD */
TAG_B, /* EQNFONT_FAT */
TAG_I /* EQNFONT_ITALIC */
};
static const struct eqn_box *
eqn_box(struct html *, const struct eqn_box *, int);
void
print_eqn(struct html *p, const struct eqn *ep)
{
struct htmlpair tag;
struct tag *t;
PAIR_CLASS_INIT(&tag, "eqn");
t = print_otag(p, TAG_MATH, 1, &tag);
p->flags |= HTML_NONOSPACE;
eqn_box(p, ep->root, 1);
p->flags &= ~HTML_NONOSPACE;
print_tagq(p, t);
}
/*
* 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
* foo bar, baz
* 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 *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);
}