1 /* $OpenBSD: eqn_html.c,v 1.6 2014/10/12 19:10:56 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 <sys/types.h> 18 19 #include <assert.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "mandoc.h" 25 #include "out.h" 26 #include "html.h" 27 28 static void 29 eqn_box(struct html *p, const struct eqn_box *bp) 30 { 31 struct tag *post, *row, *cell, *t; 32 struct htmlpair tag[2]; 33 const struct eqn_box *child, *parent; 34 size_t i, j, rows; 35 36 if (NULL == bp) 37 return; 38 39 post = NULL; 40 41 /* 42 * Special handling for a matrix, which is presented to us in 43 * column order, but must be printed in row-order. 44 */ 45 if (EQN_MATRIX == bp->type) { 46 if (NULL == bp->first) 47 goto out; 48 if (EQN_LIST != bp->first->type) { 49 eqn_box(p, bp->first); 50 goto out; 51 } 52 if (NULL == (parent = bp->first->first)) 53 goto out; 54 /* Estimate the number of rows, first. */ 55 if (NULL == (child = parent->first)) 56 goto out; 57 for (rows = 0; NULL != child; rows++) 58 child = child->next; 59 /* Print row-by-row. */ 60 post = print_otag(p, TAG_MTABLE, 0, NULL); 61 for (i = 0; i < rows; i++) { 62 parent = bp->first->first; 63 row = print_otag(p, TAG_MTR, 0, NULL); 64 while (NULL != parent) { 65 child = parent->first; 66 for (j = 0; j < i; j++) { 67 if (NULL == child) 68 break; 69 child = child->next; 70 } 71 cell = print_otag 72 (p, TAG_MTD, 0, NULL); 73 /* 74 * If we have no data for this 75 * particular cell, then print a 76 * placeholder and continue--don't puke. 77 */ 78 if (NULL != child) 79 eqn_box(p, child->first); 80 print_tagq(p, cell); 81 parent = parent->next; 82 } 83 print_tagq(p, row); 84 } 85 goto out; 86 } 87 88 switch (bp->pos) { 89 case (EQNPOS_TO): 90 post = print_otag(p, TAG_MOVER, 0, NULL); 91 break; 92 case (EQNPOS_SUP): 93 post = print_otag(p, TAG_MSUP, 0, NULL); 94 break; 95 case (EQNPOS_FROM): 96 post = print_otag(p, TAG_MUNDER, 0, NULL); 97 break; 98 case (EQNPOS_SUB): 99 post = print_otag(p, TAG_MSUB, 0, NULL); 100 break; 101 case (EQNPOS_OVER): 102 post = print_otag(p, TAG_MFRAC, 0, NULL); 103 break; 104 case (EQNPOS_FROMTO): 105 post = print_otag(p, TAG_MUNDEROVER, 0, NULL); 106 break; 107 case (EQNPOS_SUBSUP): 108 post = print_otag(p, TAG_MSUBSUP, 0, NULL); 109 break; 110 case (EQNPOS_SQRT): 111 post = print_otag(p, TAG_MSQRT, 0, NULL); 112 break; 113 default: 114 break; 115 } 116 117 if (bp->top || bp->bottom) { 118 assert(NULL == post); 119 if (bp->top && NULL == bp->bottom) 120 post = print_otag(p, TAG_MOVER, 0, NULL); 121 else if (bp->top && bp->bottom) 122 post = print_otag(p, TAG_MUNDEROVER, 0, NULL); 123 else if (bp->bottom) 124 post = print_otag(p, TAG_MUNDER, 0, NULL); 125 } 126 127 if (EQN_PILE == bp->type) { 128 assert(NULL == post); 129 if (bp->first != NULL && bp->first->type == EQN_LIST) 130 post = print_otag(p, TAG_MTABLE, 0, NULL); 131 } else if (bp->type == EQN_LIST && 132 bp->parent && bp->parent->type == EQN_PILE) { 133 assert(NULL == post); 134 post = print_otag(p, TAG_MTR, 0, NULL); 135 print_otag(p, TAG_MTD, 0, NULL); 136 } 137 138 if (NULL != bp->text) { 139 assert(NULL == post); 140 post = print_otag(p, TAG_MI, 0, NULL); 141 print_text(p, bp->text); 142 } else if (NULL == post) { 143 if (NULL != bp->left || NULL != bp->right) { 144 PAIR_INIT(&tag[0], ATTR_OPEN, 145 NULL == bp->left ? "" : bp->left); 146 PAIR_INIT(&tag[1], ATTR_CLOSE, 147 NULL == bp->right ? "" : bp->right); 148 post = print_otag(p, TAG_MFENCED, 2, tag); 149 } 150 if (NULL == post) 151 post = print_otag(p, TAG_MROW, 0, NULL); 152 else 153 print_otag(p, TAG_MROW, 0, NULL); 154 } 155 156 eqn_box(p, bp->first); 157 158 out: 159 if (NULL != bp->bottom) { 160 t = print_otag(p, TAG_MO, 0, NULL); 161 print_text(p, bp->bottom); 162 print_tagq(p, t); 163 } 164 if (NULL != bp->top) { 165 t = print_otag(p, TAG_MO, 0, NULL); 166 print_text(p, bp->top); 167 print_tagq(p, t); 168 } 169 170 if (NULL != post) 171 print_tagq(p, post); 172 173 eqn_box(p, bp->next); 174 } 175 176 void 177 print_eqn(struct html *p, const struct eqn *ep) 178 { 179 struct htmlpair tag; 180 struct tag *t; 181 182 PAIR_CLASS_INIT(&tag, "eqn"); 183 t = print_otag(p, TAG_MATH, 1, &tag); 184 185 p->flags |= HTML_NONOSPACE; 186 eqn_box(p, ep->root); 187 p->flags &= ~HTML_NONOSPACE; 188 189 print_tagq(p, t); 190 } 191