315 lines
7.4 KiB
C
315 lines
7.4 KiB
C
#include <TeX/TeX.h>
|
|
#include <TeX/structure.h>
|
|
#include <TeX/render.h>
|
|
#include <string.h>
|
|
|
|
//---
|
|
// 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);
|
|
}
|
|
}
|