/* dim.c: main layout/dimentioning routines. */
/* This file is part of asciiTeX.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
The Free Software Foundation, Inc.
59 Temple Place, Suite 330
Boston, MA 02111 USA
Authors:
Original program (eqascii): Przemek Borys
Fork by: Bart Pieters
*************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "asciiTeX_struct.h"
#include "parsedef.h"
#include "utils.h"
#include "frac.h"
#include "sscript.h"
#include "sqrt.h"
#include "ouline.h"
#include "symbols.h"
#include "brace.h"
#include "array.h"
#include "limit.h"
const KEYWORD Keys[] = {
{"^{", 2, SUPER},
{"_{", 2, SUB},
{"\\frac", 5, FRAC},
{"\\sqrt", 5, SQRT},
{"\\overline", 9, OVERLINE},
{"\\underline", 10, UNDERLINE},
{"\\limit", 6, LIMIT},
{"\\left", 5, BRACES},
{"\\begin{array}", 13, ARRAY},
{"\\to", 3, TO},
{"\\leadsto", 8, LEADSTO},
{"\\sum", 4, SUM},
{"\\prod", 5, PROD},
{"\\int", 4, INT},
{"\\oint", 5, OINT},
{"\\infty", 6, INFTY},
{"\\lceil", 6, LCEIL},
{"\\rceil", 6, RCEIL},
{"\\lfloor", 7, LFLOOR},
{"\\rfloor", 7, RFLOOR},
{"\\", 1, ESCAPE},
{NULL, ERR}
};
PRSDEF
LookupKey(char *txt, const KEYWORD * Keys)
{
for (; Keys->name; Keys++)
{
if (strncmp(txt, Keys->name, Keys->len) == 0)
break;
}
return Keys->Nr;
}
char *
findLineEnd(char *txt)
{
int len = strlen(txt);
int i;
for (i = 0; i < len; i++)
{
/* return pointer to the next endline */
if (strncmp(txt + i, "\\begin", 6) == 0) /* skip nested parts */
i = 4 + getbegin_endEnd(txt + i + 6) - txt;
else if (strncmp(txt + i, "\\left", 5) == 0)
i = 6 + findClosingLRBrace(txt + i + 5) - txt;
else if (txt[i] == '{')
i = findClosingBrace(txt + i + 1) - txt;
else if (txt[i] == '\n')
return txt + i;
}
return txt+i; /* no line end found */
}
Tdim
dim(char *txt, struct Tgraph * graph)
{
/* a linewidth mechanism were cool, i.e. automatic braking of the line */
/* baceline should jump current y down, x should be the maximum x of all lines */
/* a flag for linebreak should be placed, containing the y jump size */
/* so that the draw routines know when to add to y and reset x zo 0 */
int i;
int len = strlen(txt); /* length of text passed
* to parse */
Tdim our; /* the dimensions of our current object */
char *gpos; /* points to the tree node's text */
char *end;
PRSDEF K; /* keynumber, result from the
* keywordlookup */
our.x = 0;
our.y = 1;
our.baseline = 0;
graph->children = 0; /* at the beginning the tree doesn't have
* children. We must first find them */
graph->txt = (char *) malloc(len + 1); /* allocating the same
* length is OK. Special
* characters in output
* are 2 chars
* long--shorter than in
* the input */
gpos = graph->txt; /* we setup now this pointer */
*gpos = 0;
if (*(end=findLineEnd(txt))!='\0')
{
/* the current level contains one or more line ends */
/* the current level will become aan array of lines */
int nlines=0;
char * start=txt;
char **lines = (char **) malloc(sizeof(char *));
Tdim out;
if (SYNTAX_ERR_FLAG==S_ERR)
return out;
*gpos = 1; /* See parsedef.h for the keyword
* definitions */
gpos++;
*gpos = (char) ARRAY;
gpos++;
*gpos = 0;
newChild(graph);
graph->down[graph->children - 1]->options = malloc((2)*sizeof(char));
graph->down[graph->children - 1]->options[0] = 'c'; /* default col alignment */
graph->down[graph->children - 1]->options[1] = '\0'; /* default col alignment */
/* count how many lines we have */
while (1)
{
lines =(char **) realloc(lines,(nlines + 1) * (sizeof(char *)));
lines[nlines] = (char *) malloc(end - start + 1);
strncpy(lines[nlines], start, end - start);
lines[nlines][end - start] = '\0'; /* terminate the string */
nlines++;
if (*end=='\0')
break;
start=end+1;
end=findLineEnd(start);
}
/* fill the array with the lines */
#define Array (graph->down[graph->children-1])
Array->array = malloc(sizeof(Tarray));
Array->array->rows = nlines;
Array->array->cols = 1;
Array->array->rowy = (int *) calloc(nlines, sizeof(int));
Array->array->colx = (int *) calloc(1, sizeof(int));
for (i = 0; i < nlines; i++)
{
out = dim(lines[i], newChild(Array));
if (out.x > Array->array->colx[0])
Array->array->colx[0] = out.x;
if (out.y > Array->array->rowy[i])
Array->array->rowy[i] = out.y;
free(lines[i]);
}
free(lines);
Array->dim.x = 0;
Array->dim.x += Array->array->colx[0];
Array->dim.y = 0;
for (i = 0; i < nlines; i++)
Array->dim.y += Array->array->rowy[i];
Array->dim.y += Array->array->rows - 1;
Array->dim.x += Array->array->cols - 1;
Array->dim.baseline = Array->dim.y / 2;
our.x += Array->dim.x;
if (our.baseline < Array->dim.baseline)
{
our.y += Array->dim.baseline - our.baseline;
our.baseline = Array->dim.baseline;
}
if (our.y < Array->dim.y)
our.y = Array->dim.y;
#undef Array
graph->dim = our;
return our;
}
for (i = 0; i < len; i++)
{
if(SYNTAX_ERR_FLAG==S_ERR)
return our;
if ((txt[i] != '\\') && (txt[i] != '_') && (txt[i] != '^'))
{
our.x++;
*gpos = txt[i];
gpos++;
*gpos = 0;
} else
{
K = LookupKey(txt + i, Keys);
switch (K)
{
case SUPER:
i += dimSuperscript(txt + i, &gpos, &our,
graph);
break;
case SUB:
i += dimSubscript(txt + i, &gpos, &our,
graph);
break;
case FRAC:
i += dimFrac(txt + i, &gpos, &our, graph);
break;
case SQRT:
i += dimSqrt(txt + i, &gpos, &our, graph);
break;
case OVERLINE:
i += dimOverl(txt + i, &gpos, &our, graph);
break;
case UNDERLINE:
i += dimUnderl(txt + i, &gpos, &our,
graph);
break;
case LIMIT:
i += dimLimit(txt + i, &gpos, &our, graph);
break;
case BRACES:
i += dimBrace(txt + i, &gpos, &our, graph);
break;
case ARRAY:
i += dimArray(txt + i, &gpos, &our, graph);
break;
case TO:
i += dimTo(txt + i, &gpos, &our, graph);
break;
case LEADSTO:
i += dimLeadsto(txt + i, &gpos, &our,
graph);
break;
case SUM:
i += dimSum(txt + i, &gpos, &our, graph);
break;
case PROD:
i += dimProd(txt + i, &gpos, &our, graph);
break;
case INT:
i += dimInt(txt + i, &gpos, &our, graph);
break;
case OINT:
i += dimOint(txt + i, &gpos, &our, graph);
break;
case INFTY:
strcat(gpos, "oo");
gpos += 2;
our.x += 2;
i += 5;
break;
case RCEIL:
i += dimRceil(txt + i, &gpos, &our, graph);
break;
case LCEIL:
i += dimLceil(txt + i, &gpos, &our, graph);
break;
case RFLOOR:
i += dimRfloor(txt + i, &gpos, &our,
graph);
break;
case LFLOOR:
i += dimLfloor(txt + i, &gpos, &our,
graph);
break;
case ESCAPE:
i++;
our.x++;
*gpos = txt[i];
gpos++;
*gpos = 0;
break;
case ERR:
default:
fprintf(stderr,
"I screwed up in dim, this should never happen!\n");
exit(1);
break;
}
}
}
graph->dim = our;
return our;
}
char *
PotLineEnd(char *txt)
{
int len = strlen(txt);
int i,j;
char * plbp = "+-*/=~";
for (i = 0; i < len; i++)
{
/* return pointer to the next potential endline position */
if (strncmp(txt + i, "\\begin", 6) == 0) /* skip nested parts */
i = 4 + getbegin_endEnd(txt + i + 6) - txt;
else if (strncmp(txt + i, "\\left", 5) == 0)
i = 6 + findClosingLRBrace(txt + i + 5) - txt;
else if (txt[i] == '{')
i = findClosingBrace(txt + i + 1) - txt;
else if (txt[i] == '\\')
i++;
else if (txt[i] == '\n')
return txt + i;
else
for (j=0;j<6;j++)
if(plbp[j]==txt[i])
return txt + i + 1;
}
return txt+i; /* no potential line end found */
}
Tdim
eqdim(char *txt, struct Tgraph * graph, int ll)
{
/* if the linelength (ll) is zero we do not break, otherwise we try to fit the eq within ll
columns */
if (ll)
{
/* position linebreaks at + - / * = in the top level */
char * END=txt+strlen(txt);
struct Tgraph *dummy = malloc(sizeof(struct Tgraph));
Tdim dumdim;
char * start=txt;
char * end, c;
char * prevplb=NULL;
int x=0;
while(start<END)
{
end=PotLineEnd(start);
if (SYNTAX_ERR_FLAG==S_ERR)
return dumdim;
c=*end;
*end='\0';
InitGraph(dummy);
dumdim=dim(start,dummy);
if (SYNTAX_ERR_FLAG==S_ERR)
return dumdim;
*end=c;
dealloc(dummy);
x+=dumdim.x;
if(dumdim.x>ll)
SyntaxWarning("Warning: overfull line\n");
if((x>ll)&&(prevplb))
{
/* at the previous potential line end we have to place one */
/* in order to make it easy we create a single char line end, \n */
/* note that enters are removed in preparse to allow multi line editing
*/
*(prevplb)='\n';
x=dumdim.x;
}
if(c=='\n')
{
/* the current end is already a break */
/* reset x, prevent a new linebreak */
prevplb=NULL;
x=0;
end++;
}
else
prevplb=end;
start=end;
}
free(dummy);
}
return dim(txt, graph);
}