Annotation of src/usr.bin/mandoc/eqn_html.c, Revision 1.14
1.14 ! schwarze 1: /* $OpenBSD: eqn_html.c,v 1.13 2017/07/14 13:32:27 schwarze Exp $ */
1.1 schwarze 2: /*
1.3 schwarze 3: * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
1.7 schwarze 4: * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
1.5 schwarze 18: #include <sys/types.h>
19:
1.1 schwarze 20: #include <assert.h>
1.9 schwarze 21: #include <ctype.h>
1.1 schwarze 22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25:
26: #include "mandoc.h"
1.14 ! schwarze 27: #include "eqn.h"
1.1 schwarze 28: #include "out.h"
29: #include "html.h"
30:
1.5 schwarze 31: static void
32: eqn_box(struct html *p, const struct eqn_box *bp)
1.1 schwarze 33: {
1.5 schwarze 34: struct tag *post, *row, *cell, *t;
35: const struct eqn_box *child, *parent;
1.10 schwarze 36: const char *cp;
1.5 schwarze 37: size_t i, j, rows;
1.9 schwarze 38: enum htmltag tag;
39: enum eqn_fontt font;
1.3 schwarze 40:
41: if (NULL == bp)
1.5 schwarze 42: return;
1.3 schwarze 43:
1.5 schwarze 44: post = NULL;
1.3 schwarze 45:
46: /*
1.5 schwarze 47: * Special handling for a matrix, which is presented to us in
48: * column order, but must be printed in row-order.
1.3 schwarze 49: */
1.5 schwarze 50: if (EQN_MATRIX == bp->type) {
51: if (NULL == bp->first)
52: goto out;
1.11 schwarze 53: if (bp->first->type != EQN_LIST ||
54: bp->first->expectargs == 1) {
1.6 schwarze 55: eqn_box(p, bp->first);
56: goto out;
57: }
1.5 schwarze 58: if (NULL == (parent = bp->first->first))
59: goto out;
60: /* Estimate the number of rows, first. */
61: if (NULL == (child = parent->first))
62: goto out;
63: for (rows = 0; NULL != child; rows++)
64: child = child->next;
65: /* Print row-by-row. */
1.7 schwarze 66: post = print_otag(p, TAG_MTABLE, "");
1.5 schwarze 67: for (i = 0; i < rows; i++) {
68: parent = bp->first->first;
1.7 schwarze 69: row = print_otag(p, TAG_MTR, "");
1.5 schwarze 70: while (NULL != parent) {
71: child = parent->first;
72: for (j = 0; j < i; j++) {
73: if (NULL == child)
74: break;
75: child = child->next;
76: }
1.7 schwarze 77: cell = print_otag(p, TAG_MTD, "");
1.5 schwarze 78: /*
79: * If we have no data for this
80: * particular cell, then print a
81: * placeholder and continue--don't puke.
82: */
83: if (NULL != child)
84: eqn_box(p, child->first);
85: print_tagq(p, cell);
86: parent = parent->next;
87: }
88: print_tagq(p, row);
89: }
90: goto out;
1.4 schwarze 91: }
1.3 schwarze 92:
93: switch (bp->pos) {
1.8 schwarze 94: case EQNPOS_TO:
1.7 schwarze 95: post = print_otag(p, TAG_MOVER, "");
1.4 schwarze 96: break;
1.8 schwarze 97: case EQNPOS_SUP:
1.7 schwarze 98: post = print_otag(p, TAG_MSUP, "");
1.3 schwarze 99: break;
1.8 schwarze 100: case EQNPOS_FROM:
1.7 schwarze 101: post = print_otag(p, TAG_MUNDER, "");
1.4 schwarze 102: break;
1.8 schwarze 103: case EQNPOS_SUB:
1.7 schwarze 104: post = print_otag(p, TAG_MSUB, "");
1.3 schwarze 105: break;
1.8 schwarze 106: case EQNPOS_OVER:
1.7 schwarze 107: post = print_otag(p, TAG_MFRAC, "");
1.3 schwarze 108: break;
1.8 schwarze 109: case EQNPOS_FROMTO:
1.7 schwarze 110: post = print_otag(p, TAG_MUNDEROVER, "");
1.4 schwarze 111: break;
1.8 schwarze 112: case EQNPOS_SUBSUP:
1.7 schwarze 113: post = print_otag(p, TAG_MSUBSUP, "");
1.5 schwarze 114: break;
1.8 schwarze 115: case EQNPOS_SQRT:
1.7 schwarze 116: post = print_otag(p, TAG_MSQRT, "");
1.3 schwarze 117: break;
118: default:
119: break;
120: }
121:
1.5 schwarze 122: if (bp->top || bp->bottom) {
123: assert(NULL == post);
124: if (bp->top && NULL == bp->bottom)
1.7 schwarze 125: post = print_otag(p, TAG_MOVER, "");
1.5 schwarze 126: else if (bp->top && bp->bottom)
1.7 schwarze 127: post = print_otag(p, TAG_MUNDEROVER, "");
1.5 schwarze 128: else if (bp->bottom)
1.7 schwarze 129: post = print_otag(p, TAG_MUNDER, "");
1.5 schwarze 130: }
131:
132: if (EQN_PILE == bp->type) {
133: assert(NULL == post);
1.11 schwarze 134: if (bp->first != NULL &&
135: bp->first->type == EQN_LIST &&
136: bp->first->expectargs > 1)
1.7 schwarze 137: post = print_otag(p, TAG_MTABLE, "");
1.11 schwarze 138: } else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
1.6 schwarze 139: bp->parent && bp->parent->type == EQN_PILE) {
1.5 schwarze 140: assert(NULL == post);
1.7 schwarze 141: post = print_otag(p, TAG_MTR, "");
142: print_otag(p, TAG_MTD, "");
1.5 schwarze 143: }
1.3 schwarze 144:
1.9 schwarze 145: if (bp->text != NULL) {
146: assert(post == NULL);
147: tag = TAG_MI;
1.10 schwarze 148: cp = bp->text;
149: if (isdigit((unsigned char)cp[0]) ||
150: (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
1.9 schwarze 151: tag = TAG_MN;
152: while (*++cp != '\0') {
1.10 schwarze 153: if (*cp != '.' &&
154: isdigit((unsigned char)*cp) == 0) {
1.9 schwarze 155: tag = TAG_MI;
156: break;
157: }
158: }
1.10 schwarze 159: } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
1.9 schwarze 160: tag = TAG_MO;
1.10 schwarze 161: while (*cp != '\0') {
162: if (cp[0] == '\\' && cp[1] != '\0') {
163: cp++;
164: mandoc_escape(&cp, NULL, NULL);
165: } else if (isalnum((unsigned char)*cp)) {
1.9 schwarze 166: tag = TAG_MI;
167: break;
1.10 schwarze 168: } else
169: cp++;
1.9 schwarze 170: }
171: }
172: font = bp->font;
173: if (bp->text[0] != '\0' &&
174: (((tag == TAG_MN || tag == TAG_MO) &&
175: font == EQNFONT_ROMAN) ||
176: (tag == TAG_MI && font == (bp->text[1] == '\0' ?
177: EQNFONT_ITALIC : EQNFONT_ROMAN))))
178: font = EQNFONT_NONE;
179: switch (font) {
180: case EQNFONT_NONE:
181: post = print_otag(p, tag, "");
182: break;
183: case EQNFONT_ROMAN:
184: post = print_otag(p, tag, "?", "fontstyle", "normal");
185: break;
186: case EQNFONT_BOLD:
187: case EQNFONT_FAT:
188: post = print_otag(p, tag, "?", "fontweight", "bold");
189: break;
190: case EQNFONT_ITALIC:
191: post = print_otag(p, tag, "?", "fontstyle", "italic");
192: break;
193: default:
194: abort();
195: }
1.5 schwarze 196: print_text(p, bp->text);
197: } else if (NULL == post) {
1.7 schwarze 198: if (NULL != bp->left || NULL != bp->right)
199: post = print_otag(p, TAG_MFENCED, "??",
200: "open", bp->left == NULL ? "" : bp->left,
201: "close", bp->right == NULL ? "" : bp->right);
1.5 schwarze 202: if (NULL == post)
1.7 schwarze 203: post = print_otag(p, TAG_MROW, "");
1.5 schwarze 204: else
1.7 schwarze 205: print_otag(p, TAG_MROW, "");
1.3 schwarze 206: }
207:
1.5 schwarze 208: eqn_box(p, bp->first);
209:
210: out:
211: if (NULL != bp->bottom) {
1.7 schwarze 212: t = print_otag(p, TAG_MO, "");
1.5 schwarze 213: print_text(p, bp->bottom);
214: print_tagq(p, t);
215: }
216: if (NULL != bp->top) {
1.7 schwarze 217: t = print_otag(p, TAG_MO, "");
1.5 schwarze 218: print_text(p, bp->top);
219: print_tagq(p, t);
220: }
221:
222: if (NULL != post)
1.3 schwarze 223: print_tagq(p, post);
224:
1.5 schwarze 225: eqn_box(p, bp->next);
226: }
227:
228: void
1.12 schwarze 229: print_eqn(struct html *p, const struct eqn_box *bp)
1.5 schwarze 230: {
231: struct tag *t;
1.13 schwarze 232:
233: if (bp->first == NULL)
234: return;
1.5 schwarze 235:
1.7 schwarze 236: t = print_otag(p, TAG_MATH, "c", "eqn");
1.5 schwarze 237:
238: p->flags |= HTML_NONOSPACE;
1.12 schwarze 239: eqn_box(p, bp);
1.5 schwarze 240: p->flags &= ~HTML_NONOSPACE;
1.3 schwarze 241:
1.5 schwarze 242: print_tagq(p, t);
1.1 schwarze 243: }