#include #include #include #include //--- // Chunks to place subscripts and superscripts //--- #define TEX_CHUNK_INLINE 0 #define TEX_CHUNK_DISPLAY 1 #define TEX_CHUNK_SLANTED 2 /* struct chunk: Chunk of nodes positioned relative to each other The notion of "chunk" comprises a base with a subscript and a superscript. Together they can be arranged in three different ways: * TEX_CHUNK_INLINE: Normal inline setting, subscript and superscript are placed to the right of the base and moved below and above the baseline. * TEX_CHUNK_DISPLAY: Similar to LaTeX's display style, subscript and superscript are placed under and above the base, and are centered horizontally. * TEX_CHUNK_SLANTED: Similar to inline mode, but subscript is slightly moved to the left and superscript is slightly moved to the right. Looks good on integrals. The user of a chunk sets the [x] and [l] members of the [base] node, then selects the display mode and optionally adds subscript and superscript. When the chunk_finalize() function is called, the position of the elements is computed and the [x] and [l] members of the [sub] and [sup] nodes, as well as the geometry of the chunk, are set. */ struct chunk { /* Base node, baseline aligned on the flow */ struct TeX_Node *base; /* Subscript and superscript (might both be NULL) */ struct TeX_Node *sub, *sup; /* Display mode */ int mode; /* Chunk geometry */ int width; int height; int line; }; /* chunk_make(): Make a new chunk for a base node */ void chunk_make(struct chunk *chunk, struct TeX_Node *base, int mode) { chunk->base = base; chunk->mode = mode; chunk->sub = NULL; chunk->sup = NULL; chunk->width = 0; chunk->height = 0; chunk->line = 0; } /* chunk_reset(): Empty a chunk */ void chunk_reset(struct chunk *chunk) { memset(chunk, 0, sizeof *chunk); } /* chunk_set(): Check whether a chunk is empty */ int chunk_set(struct chunk *chunk) { return chunk->base != NULL; } /* chunk_set_subscript(): Add a subscript on a chunk */ void chunk_set_subscript(struct chunk *chunk, struct TeX_Node *sub) { /* Silently refuse double index */ if(chunk->sub) return; chunk->sub = sub; } /* chunk_set_superscript(): Add a superscript on a chunk */ void chunk_set_superscript(struct chunk *chunk, struct TeX_Node *sup) { /* Silently refuse double exponent */ if(chunk->sup) return; chunk->sup = sup; } /* chunk_compute_inline(): Compute chunk layout for inline mode */ void chunk_compute_inline(struct chunk *chunk) { struct TeX_Node *base = chunk->base; struct TeX_Node *sup = chunk->sup; struct TeX_Node *sub = chunk->sub; /* Height over and below baseline */ int over = base->line; int under = base->height - base->line; /* Additional width */ int right = 0; if(sub) { int elevation = TEX_SUBSCRIPT_ELEVATION; sub->x = base->x + base->width + TEX_LAYOUT_SPACING; sub->l = base->l + (base->height - base->line + sub->line - elevation); under = max(under, under + sub->height - elevation); right = max(right, sub->width); } if(sup) { int depth = TEX_SUPERSCRIPT_DEPTH; sup->x = base->x + base->width + TEX_LAYOUT_SPACING; sup->l = base->l - (base->line + (sup->height - sup->line) - depth); over = max(over, over + sup->height - depth); right = max(right, sup->width); } if(right > 0) right += TEX_LAYOUT_SPACING; chunk->width = base->width + right; chunk->height = over + under; chunk->line = over; } /* chunk_compute_display(): Compute chunk layout for display mode */ void chunk_compute_display(struct chunk *chunk) { /* TODO: Display mode for exponents and subscripts */ chunk_compute_inline(chunk); } /* chunk_compute_slanted(): Compute chunk layout for slanted mode */ void chunk_compute_slanted(struct chunk *chunk) { /* TODO: Slanted mode for exponents and subscripts */ chunk_compute_inline(chunk); } /* chunk_compute(): Compute the layout of a chunk after it's filled */ void chunk_compute(struct chunk *chunk) { if(chunk->mode == TEX_CHUNK_INLINE) { chunk_compute_inline(chunk); } if(chunk->mode == TEX_CHUNK_DISPLAY) { chunk_compute_display(chunk); } if(chunk->mode == TEX_CHUNK_SLANTED) { chunk_compute_slanted(chunk); } } //--- // Groups to compute the size of parenthesis-like elements //--- struct group { /* Opening parenthesis/bracket/whatever */ struct TeX_Node *left; /* Maximum height above and below baseline */ int above; int below; }; /* group_open(): Create new group initialized with a left node */ void group_open(struct group *group, struct TeX_Node *left) { group->left = left; group->above = 0; group->below = 0; } /* group_update(): Update a group with a new node */ void group_update(struct group *group, struct TeX_Node *node) { /* Account for node displacement around baseline */ group->above = max(node->line - node->l, group->above); group->below = max(node->height - node->line + node->l, group->below); } /* group_close(): Close a group with its right node */ void group_close(struct group *group, struct TeX_Node *right) { struct TeX_Node *left = group->left; int above = group->above; int below = group->below; int line = above; #if TEX_LEFTRIGHT_SYMMETRICAL above = below = max(above, below); #endif #if ! TEX_LEFTRIGHT_ALIGNED line = (above + below) >> 1; #endif /* Set the height of the left and right node */ left->height = above + below; left->line = line; left->l += (line - above); right->height = above + below; right->line = line; right->l += (line - above); } //--- // Laying out flows //--- static void update(struct chunk *c, int *x, int *above, int *below) { if(!chunk_set(c)) return; chunk_compute(c); *x += c->width + TEX_LAYOUT_SPACING; *above = max(*above, c->line - c->base->l); *below = max(*below, c->height - c->line + c->base->l); chunk_reset(c); } /* size_flow(): Calculate the layout of a flow */ void size_flow(struct TeX_Flow *flow) { /* Current node and current chunk */ struct TeX_Node *node; struct chunk c = { NULL }; /* Position in flow */ int x = 0; /* Height above baseline (excluded), and below baseline (included) */ int above = 0; int below = 0; /* Nested groups and current position */ struct group groups[TEX_LEFTRIGHT_NESTING]; struct group *g = groups; for(node = flow->first; node; node = node->next) { char const *class = ""; size_node(node); group_update(g, node); if(node->type != TEX_NODECLASS_TEXT) class = TeX_table[node->type - 1].name; if(!strcmp(class, "\\sup")) { chunk_set_superscript(&c, node); continue; } if(!strcmp(class, "\\sub")) { chunk_set_subscript(&c, node); continue; } if(!strcmp(class, "left") && g-groups < TEX_LEFTRIGHT_NESTING) { g++; group_open(g, node); group_update(g, node); } if(!strcmp(class, "right") && g - groups > 0) { group_close(g, node); g--; } /* Leave the last chunk and make a new one */ update(&c, &x, &above, &below); node->x = x; chunk_make(&c, node, TEX_CHUNK_INLINE); } /* Finish the last chunk */ update(&c, &x, &above, &below); flow->height = above + below; flow->line = above; flow->width = max(x - TEX_LAYOUT_SPACING, 0); } //--- // Rendering flows //--- /* render_flow() - Render a flow and all its components */ void render_flow(struct TeX_Flow const * flow, int x, int y, int color) { struct TeX_Node const * node; for(node = flow->first; node; node = node->next) { render_node(node, x + node->x, y + flow->line - node->line + node->l, color); } }