1 /* $Id: eqn_html.c,v 1.10 2014/10/12 19:31:41 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include "config.h" 18 19 #include <sys/types.h> 20 21 #include <assert.h> 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 30 static void 31 eqn_box(struct html *p, const struct eqn_box *bp) 32 { 33 struct tag *post, *row, *cell, *t; 34 struct htmlpair tag[2]; 35 const struct eqn_box *child, *parent; 36 size_t i, j, rows; 37 38 if (NULL == bp) 39 return; 40 41 post = NULL; 42 43 /* 44 * Special handling for a matrix, which is presented to us in 45 * column order, but must be printed in row-order. 46 */ 47 if (EQN_MATRIX == bp->type) { 48 if (NULL == bp->first) 49 goto out; 50 if (EQN_LIST != bp->first->type) { 51 eqn_box(p, bp->first); 52 goto out; 53 } 54 if (NULL == (parent = bp->first->first)) 55 goto out; 56 /* Estimate the number of rows, first. */ 57 if (NULL == (child = parent->first)) 58 goto out; 59 for (rows = 0; NULL != child; rows++) 60 child = child->next; 61 /* Print row-by-row. */ 62 post = print_otag(p, TAG_MTABLE, 0, NULL); 63 for (i = 0; i < rows; i++) { 64 parent = bp->first->first; 65 row = print_otag(p, TAG_MTR, 0, NULL); 66 while (NULL != parent) { 67 child = parent->first; 68 for (j = 0; j < i; j++) { 69 if (NULL == child) 70 break; 71 child = child->next; 72 } 73 cell = print_otag 74 (p, TAG_MTD, 0, NULL); 75 /* 76 * If we have no data for this 77 * particular cell, then print a 78 * placeholder and continue--don't puke. 79 */ 80 if (NULL != child) 81 eqn_box(p, child->first); 82 print_tagq(p, cell); 83 parent = parent->next; 84 } 85 print_tagq(p, row); 86 } 87 goto out; 88 } 89 90 switch (bp->pos) { 91 case (EQNPOS_TO): 92 post = print_otag(p, TAG_MOVER, 0, NULL); 93 break; 94 case (EQNPOS_SUP): 95 post = print_otag(p, TAG_MSUP, 0, NULL); 96 break; 97 case (EQNPOS_FROM): 98 post = print_otag(p, TAG_MUNDER, 0, NULL); 99 break; 100 case (EQNPOS_SUB): 101 post = print_otag(p, TAG_MSUB, 0, NULL); 102 break; 103 case (EQNPOS_OVER): 104 post = print_otag(p, TAG_MFRAC, 0, NULL); 105 break; 106 case (EQNPOS_FROMTO): 107 post = print_otag(p, TAG_MUNDEROVER, 0, NULL); 108 break; 109 case (EQNPOS_SUBSUP): 110 post = print_otag(p, TAG_MSUBSUP, 0, NULL); 111 break; 112 case (EQNPOS_SQRT): 113 post = print_otag(p, TAG_MSQRT, 0, NULL); 114 break; 115 default: 116 break; 117 } 118 119 if (bp->top || bp->bottom) { 120 assert(NULL == post); 121 if (bp->top && NULL == bp->bottom) 122 post = print_otag(p, TAG_MOVER, 0, NULL); 123 else if (bp->top && bp->bottom) 124 post = print_otag(p, TAG_MUNDEROVER, 0, NULL); 125 else if (bp->bottom) 126 post = print_otag(p, TAG_MUNDER, 0, NULL); 127 } 128 129 if (EQN_PILE == bp->type) { 130 assert(NULL == post); 131 if (bp->first != NULL && bp->first->type == EQN_LIST) 132 post = print_otag(p, TAG_MTABLE, 0, NULL); 133 } else if (bp->type == EQN_LIST && 134 bp->parent && bp->parent->type == EQN_PILE) { 135 assert(NULL == post); 136 post = print_otag(p, TAG_MTR, 0, NULL); 137 print_otag(p, TAG_MTD, 0, NULL); 138 } 139 140 if (NULL != bp->text) { 141 assert(NULL == post); 142 post = print_otag(p, TAG_MI, 0, NULL); 143 print_text(p, bp->text); 144 } else if (NULL == post) { 145 if (NULL != bp->left || NULL != bp->right) { 146 PAIR_INIT(&tag[0], ATTR_OPEN, 147 NULL == bp->left ? "" : bp->left); 148 PAIR_INIT(&tag[1], ATTR_CLOSE, 149 NULL == bp->right ? "" : bp->right); 150 post = print_otag(p, TAG_MFENCED, 2, tag); 151 } 152 if (NULL == post) 153 post = print_otag(p, TAG_MROW, 0, NULL); 154 else 155 print_otag(p, TAG_MROW, 0, NULL); 156 } 157 158 eqn_box(p, bp->first); 159 160 out: 161 if (NULL != bp->bottom) { 162 t = print_otag(p, TAG_MO, 0, NULL); 163 print_text(p, bp->bottom); 164 print_tagq(p, t); 165 } 166 if (NULL != bp->top) { 167 t = print_otag(p, TAG_MO, 0, NULL); 168 print_text(p, bp->top); 169 print_tagq(p, t); 170 } 171 172 if (NULL != post) 173 print_tagq(p, post); 174 175 eqn_box(p, bp->next); 176 } 177 178 void 179 print_eqn(struct html *p, const struct eqn *ep) 180 { 181 struct htmlpair tag; 182 struct tag *t; 183 184 PAIR_CLASS_INIT(&tag, "eqn"); 185 t = print_otag(p, TAG_MATH, 1, &tag); 186 187 p->flags |= HTML_NONOSPACE; 188 eqn_box(p, ep->root); 189 p->flags &= ~HTML_NONOSPACE; 190 191 print_tagq(p, t); 192 } 193