Annotation of src/usr.bin/mandoc/eqn_html.c, Revision 1.13
1.13 ! schwarze 1: /* $OpenBSD: eqn_html.c,v 1.12 2017/07/08 14:51:01 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"
27: #include "out.h"
28: #include "html.h"
29:
1.5 schwarze 30: static void
31: eqn_box(struct html *p, const struct eqn_box *bp)
1.1 schwarze 32: {
1.5 schwarze 33: struct tag *post, *row, *cell, *t;
34: const struct eqn_box *child, *parent;
1.10 schwarze 35: const char *cp;
1.5 schwarze 36: size_t i, j, rows;
1.9 schwarze 37: enum htmltag tag;
38: enum eqn_fontt font;
1.3 schwarze 39:
40: if (NULL == bp)
1.5 schwarze 41: return;
1.3 schwarze 42:
1.5 schwarze 43: post = NULL;
1.3 schwarze 44:
45: /*
1.5 schwarze 46: * Special handling for a matrix, which is presented to us in
47: * column order, but must be printed in row-order.
1.3 schwarze 48: */
1.5 schwarze 49: if (EQN_MATRIX == bp->type) {
50: if (NULL == bp->first)
51: goto out;
1.11 schwarze 52: if (bp->first->type != EQN_LIST ||
53: bp->first->expectargs == 1) {
1.6 schwarze 54: eqn_box(p, bp->first);
55: goto out;
56: }
1.5 schwarze 57: if (NULL == (parent = bp->first->first))
58: goto out;
59: /* Estimate the number of rows, first. */
60: if (NULL == (child = parent->first))
61: goto out;
62: for (rows = 0; NULL != child; rows++)
63: child = child->next;
64: /* Print row-by-row. */
1.7 schwarze 65: post = print_otag(p, TAG_MTABLE, "");
1.5 schwarze 66: for (i = 0; i < rows; i++) {
67: parent = bp->first->first;
1.7 schwarze 68: row = print_otag(p, TAG_MTR, "");
1.5 schwarze 69: while (NULL != parent) {
70: child = parent->first;
71: for (j = 0; j < i; j++) {
72: if (NULL == child)
73: break;
74: child = child->next;
75: }
1.7 schwarze 76: cell = print_otag(p, TAG_MTD, "");
1.5 schwarze 77: /*
78: * If we have no data for this
79: * particular cell, then print a
80: * placeholder and continue--don't puke.
81: */
82: if (NULL != child)
83: eqn_box(p, child->first);
84: print_tagq(p, cell);
85: parent = parent->next;
86: }
87: print_tagq(p, row);
88: }
89: goto out;
1.4 schwarze 90: }
1.3 schwarze 91:
92: switch (bp->pos) {
1.8 schwarze 93: case EQNPOS_TO:
1.7 schwarze 94: post = print_otag(p, TAG_MOVER, "");
1.4 schwarze 95: break;
1.8 schwarze 96: case EQNPOS_SUP:
1.7 schwarze 97: post = print_otag(p, TAG_MSUP, "");
1.3 schwarze 98: break;
1.8 schwarze 99: case EQNPOS_FROM:
1.7 schwarze 100: post = print_otag(p, TAG_MUNDER, "");
1.4 schwarze 101: break;
1.8 schwarze 102: case EQNPOS_SUB:
1.7 schwarze 103: post = print_otag(p, TAG_MSUB, "");
1.3 schwarze 104: break;
1.8 schwarze 105: case EQNPOS_OVER:
1.7 schwarze 106: post = print_otag(p, TAG_MFRAC, "");
1.3 schwarze 107: break;
1.8 schwarze 108: case EQNPOS_FROMTO:
1.7 schwarze 109: post = print_otag(p, TAG_MUNDEROVER, "");
1.4 schwarze 110: break;
1.8 schwarze 111: case EQNPOS_SUBSUP:
1.7 schwarze 112: post = print_otag(p, TAG_MSUBSUP, "");
1.5 schwarze 113: break;
1.8 schwarze 114: case EQNPOS_SQRT:
1.7 schwarze 115: post = print_otag(p, TAG_MSQRT, "");
1.3 schwarze 116: break;
117: default:
118: break;
119: }
120:
1.5 schwarze 121: if (bp->top || bp->bottom) {
122: assert(NULL == post);
123: if (bp->top && NULL == bp->bottom)
1.7 schwarze 124: post = print_otag(p, TAG_MOVER, "");
1.5 schwarze 125: else if (bp->top && bp->bottom)
1.7 schwarze 126: post = print_otag(p, TAG_MUNDEROVER, "");
1.5 schwarze 127: else if (bp->bottom)
1.7 schwarze 128: post = print_otag(p, TAG_MUNDER, "");
1.5 schwarze 129: }
130:
131: if (EQN_PILE == bp->type) {
132: assert(NULL == post);
1.11 schwarze 133: if (bp->first != NULL &&
134: bp->first->type == EQN_LIST &&
135: bp->first->expectargs > 1)
1.7 schwarze 136: post = print_otag(p, TAG_MTABLE, "");
1.11 schwarze 137: } else if (bp->type == EQN_LIST && bp->expectargs > 1 &&
1.6 schwarze 138: bp->parent && bp->parent->type == EQN_PILE) {
1.5 schwarze 139: assert(NULL == post);
1.7 schwarze 140: post = print_otag(p, TAG_MTR, "");
141: print_otag(p, TAG_MTD, "");
1.5 schwarze 142: }
1.3 schwarze 143:
1.9 schwarze 144: if (bp->text != NULL) {
145: assert(post == NULL);
146: tag = TAG_MI;
1.10 schwarze 147: cp = bp->text;
148: if (isdigit((unsigned char)cp[0]) ||
149: (cp[0] == '.' && isdigit((unsigned char)cp[1]))) {
1.9 schwarze 150: tag = TAG_MN;
151: while (*++cp != '\0') {
1.10 schwarze 152: if (*cp != '.' &&
153: isdigit((unsigned char)*cp) == 0) {
1.9 schwarze 154: tag = TAG_MI;
155: break;
156: }
157: }
1.10 schwarze 158: } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) {
1.9 schwarze 159: tag = TAG_MO;
1.10 schwarze 160: while (*cp != '\0') {
161: if (cp[0] == '\\' && cp[1] != '\0') {
162: cp++;
163: mandoc_escape(&cp, NULL, NULL);
164: } else if (isalnum((unsigned char)*cp)) {
1.9 schwarze 165: tag = TAG_MI;
166: break;
1.10 schwarze 167: } else
168: cp++;
1.9 schwarze 169: }
170: }
171: font = bp->font;
172: if (bp->text[0] != '\0' &&
173: (((tag == TAG_MN || tag == TAG_MO) &&
174: font == EQNFONT_ROMAN) ||
175: (tag == TAG_MI && font == (bp->text[1] == '\0' ?
176: EQNFONT_ITALIC : EQNFONT_ROMAN))))
177: font = EQNFONT_NONE;
178: switch (font) {
179: case EQNFONT_NONE:
180: post = print_otag(p, tag, "");
181: break;
182: case EQNFONT_ROMAN:
183: post = print_otag(p, tag, "?", "fontstyle", "normal");
184: break;
185: case EQNFONT_BOLD:
186: case EQNFONT_FAT:
187: post = print_otag(p, tag, "?", "fontweight", "bold");
188: break;
189: case EQNFONT_ITALIC:
190: post = print_otag(p, tag, "?", "fontstyle", "italic");
191: break;
192: default:
193: abort();
194: }
1.5 schwarze 195: print_text(p, bp->text);
196: } else if (NULL == post) {
1.7 schwarze 197: if (NULL != bp->left || NULL != bp->right)
198: post = print_otag(p, TAG_MFENCED, "??",
199: "open", bp->left == NULL ? "" : bp->left,
200: "close", bp->right == NULL ? "" : bp->right);
1.5 schwarze 201: if (NULL == post)
1.7 schwarze 202: post = print_otag(p, TAG_MROW, "");
1.5 schwarze 203: else
1.7 schwarze 204: print_otag(p, TAG_MROW, "");
1.3 schwarze 205: }
206:
1.5 schwarze 207: eqn_box(p, bp->first);
208:
209: out:
210: if (NULL != bp->bottom) {
1.7 schwarze 211: t = print_otag(p, TAG_MO, "");
1.5 schwarze 212: print_text(p, bp->bottom);
213: print_tagq(p, t);
214: }
215: if (NULL != bp->top) {
1.7 schwarze 216: t = print_otag(p, TAG_MO, "");
1.5 schwarze 217: print_text(p, bp->top);
218: print_tagq(p, t);
219: }
220:
221: if (NULL != post)
1.3 schwarze 222: print_tagq(p, post);
223:
1.5 schwarze 224: eqn_box(p, bp->next);
225: }
226:
227: void
1.12 schwarze 228: print_eqn(struct html *p, const struct eqn_box *bp)
1.5 schwarze 229: {
230: struct tag *t;
1.13 ! schwarze 231:
! 232: if (bp->first == NULL)
! 233: return;
1.5 schwarze 234:
1.7 schwarze 235: t = print_otag(p, TAG_MATH, "c", "eqn");
1.5 schwarze 236:
237: p->flags |= HTML_NONOSPACE;
1.12 schwarze 238: eqn_box(p, bp);
1.5 schwarze 239: p->flags &= ~HTML_NONOSPACE;
1.3 schwarze 240:
1.5 schwarze 241: print_tagq(p, t);
1.1 schwarze 242: }