1 /* dim.c: main layout/dimentioning routines. */
2 
3 /*  This file is part of asciiTeX.
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; see the file COPYING.  If not, write to
16       The Free Software Foundation, Inc.
17       59 Temple Place, Suite 330
18       Boston, MA 02111 USA
19 
20 
21     Authors:
22     Original program (eqascii): Przemek Borys
23     Fork by: Bart Pieters
24 
25 *************************************************************************/
26 
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include "asciiTeX_struct.h"
31 #include "parsedef.h"
32 #include "utils.h"
33 #include "frac.h"
34 #include "sscript.h"
35 #include "sqrt.h"
36 #include "ouline.h"
37 #include "symbols.h"
38 #include "brace.h"
39 #include "array.h"
40 #include "limit.h"
41 
42 const KEYWORD   Keys[] = {
43 	{"^{", 2, SUPER},
44 	{"_{", 2, SUB},
45 	{"\\frac", 5, FRAC},
46 	{"\\sqrt", 5, SQRT},
47 	{"\\overline", 9, OVERLINE},
48 	{"\\underline", 10, UNDERLINE},
49 	{"\\limit", 6, LIMIT},
50 	{"\\left", 5, BRACES},
51 	{"\\begin{array}", 13, ARRAY},
52 	{"\\to", 3, TO},
53 	{"\\leadsto", 8, LEADSTO},
54 	{"\\sum", 4, SUM},
55 	{"\\prod", 5, PROD},
56 	{"\\int", 4, INT},
57 	{"\\oint", 5, OINT},
58 	{"\\infty", 6, INFTY},
59 	{"\\lceil", 6, LCEIL},
60 	{"\\rceil", 6, RCEIL},
61 	{"\\lfloor", 7, LFLOOR},
62 	{"\\rfloor", 7, RFLOOR},
63 	{"\\", 1, ESCAPE},
64 	{NULL, ERR}
65 };
66 PRSDEF
LookupKey(char * txt,const KEYWORD * Keys)67 LookupKey(char *txt, const KEYWORD * Keys)
68 {
69 	for (; Keys->name; Keys++)
70 	{
71 		if (strncmp(txt, Keys->name, Keys->len) == 0)
72 			break;
73 	}
74 	return Keys->Nr;
75 }
76 
77 char           *
findLineEnd(char * txt)78 findLineEnd(char *txt)
79 {
80 	int             len = strlen(txt);
81 	int             i;
82 	for (i = 0; i < len; i++)
83 	{
84 		/* return pointer to the next endline */
85 		if (strncmp(txt + i, "\\begin", 6) == 0)	/* skip nested parts */
86 			i = 4 + getbegin_endEnd(txt + i + 6) - txt;
87 		else if (strncmp(txt + i, "\\left", 5) == 0)
88 			i = 6 + findClosingLRBrace(txt + i + 5) - txt;
89 		else if (txt[i] == '{')
90 			i = findClosingBrace(txt + i + 1) - txt;
91 		else if (txt[i] == '\n')
92 			return txt + i;
93 
94 	}
95 	return txt+i;		/* no line end found */
96 }
97 
98 Tdim
dim(char * txt,struct Tgraph * graph)99 dim(char *txt, struct Tgraph * graph)
100 {
101 	/* a linewidth mechanism were cool, i.e. automatic braking of the line */
102 	/* baceline should jump current y down, x should be the maximum x of all lines */
103 	/* a flag for linebreak should be placed, containing the y jump size */
104 	/* so that the draw routines know when to add to y and reset x zo 0 */
105 	int             i;
106 	int             len = strlen(txt);	/* length of text passed
107 						 * to parse */
108 	Tdim            our;	/* the dimensions of our current object */
109 	char           *gpos;	/* points to the tree node's text */
110 	char *end;
111 	PRSDEF          K;	/* keynumber, result from the
112 				 * keywordlookup */
113 	our.x = 0;
114 	our.y = 1;
115 	our.baseline = 0;
116 	graph->children = 0;	/* at the beginning the tree doesn't have
117 				 * children. We must first find them */
118 	graph->txt = (char *) malloc(len + 1);	/* allocating the same
119 						 * length is OK. Special
120 						 * characters in output
121 						 * are 2 chars
122 						 * long--shorter than in
123 						 * the input */
124 	gpos = graph->txt;	/* we setup now this pointer */
125 	*gpos = 0;
126 	if (*(end=findLineEnd(txt))!='\0')
127 	{
128 		/* the current level contains one or more line ends */
129 		/* the current level will become aan array of lines */
130 		int nlines=0;
131 		char * start=txt;
132 		char          **lines = (char **) malloc(sizeof(char *));
133 		Tdim            out;
134 		if (SYNTAX_ERR_FLAG==S_ERR)
135 			return out;
136 		*gpos = 1;		/* See parsedef.h for the keyword
137 				 * definitions */
138 		gpos++;
139 		*gpos = (char) ARRAY;
140 		gpos++;
141 		*gpos = 0;
142 		newChild(graph);
143 		graph->down[graph->children - 1]->options = malloc((2)*sizeof(char));
144 		graph->down[graph->children - 1]->options[0] = 'c'; /* default col alignment */
145 		graph->down[graph->children - 1]->options[1] = '\0'; /* default col alignment */
146 		/* count how many lines we have */
147 		while (1)
148 		{
149 			lines =(char **) realloc(lines,(nlines + 1) * (sizeof(char *)));
150 			lines[nlines] = (char *) malloc(end - start + 1);
151 			strncpy(lines[nlines], start, end - start);
152 			lines[nlines][end - start] = '\0';	/* terminate the string */
153 			nlines++;
154 			if (*end=='\0')
155 				break;
156 			start=end+1;
157 			end=findLineEnd(start);
158 		}
159 		/* fill the array with the lines */
160 
161 #define Array (graph->down[graph->children-1])
162 		Array->array = malloc(sizeof(Tarray));
163 		Array->array->rows = nlines;
164 		Array->array->cols = 1;
165 		Array->array->rowy = (int *) calloc(nlines, sizeof(int));
166 		Array->array->colx = (int *) calloc(1, sizeof(int));
167 		for (i = 0; i < nlines; i++)
168 		{
169 			out = dim(lines[i], newChild(Array));
170 			if (out.x > Array->array->colx[0])
171 				Array->array->colx[0] = out.x;
172 			if (out.y > Array->array->rowy[i])
173 				Array->array->rowy[i] = out.y;
174 			free(lines[i]);
175 		}
176 		free(lines);
177 		Array->dim.x = 0;
178 		Array->dim.x += Array->array->colx[0];
179 		Array->dim.y = 0;
180 		for (i = 0; i < nlines; i++)
181 			Array->dim.y += Array->array->rowy[i];
182 
183 		Array->dim.y += Array->array->rows - 1;
184 		Array->dim.x += Array->array->cols - 1;
185 
186 		Array->dim.baseline = Array->dim.y / 2;
187 
188 		our.x += Array->dim.x;
189 		if (our.baseline < Array->dim.baseline)
190 		{
191 			our.y += Array->dim.baseline - our.baseline;
192 			our.baseline = Array->dim.baseline;
193 		}
194 		if (our.y < Array->dim.y)
195 			our.y = Array->dim.y;
196 #undef Array
197 		graph->dim = our;
198 		return our;
199 	}
200 	for (i = 0; i < len; i++)
201 	{
202 		if(SYNTAX_ERR_FLAG==S_ERR)
203 			return our;
204 		if ((txt[i] != '\\') && (txt[i] != '_') && (txt[i] != '^'))
205 		{
206 			our.x++;
207 			*gpos = txt[i];
208 			gpos++;
209 			*gpos = 0;
210 		} else
211 		{
212 			K = LookupKey(txt + i, Keys);
213 			switch (K)
214 			{
215 			case SUPER:
216 				i += dimSuperscript(txt + i, &gpos, &our,
217 						    graph);
218 				break;
219 			case SUB:
220 				i += dimSubscript(txt + i, &gpos, &our,
221 						  graph);
222 				break;
223 			case FRAC:
224 				i += dimFrac(txt + i, &gpos, &our, graph);
225 				break;
226 			case SQRT:
227 				i += dimSqrt(txt + i, &gpos, &our, graph);
228 				break;
229 			case OVERLINE:
230 				i += dimOverl(txt + i, &gpos, &our, graph);
231 				break;
232 			case UNDERLINE:
233 				i += dimUnderl(txt + i, &gpos, &our,
234 					       graph);
235 				break;
236 			case LIMIT:
237 				i += dimLimit(txt + i, &gpos, &our, graph);
238 				break;
239 			case BRACES:
240 				i += dimBrace(txt + i, &gpos, &our, graph);
241 				break;
242 			case ARRAY:
243 				i += dimArray(txt + i, &gpos, &our, graph);
244 				break;
245 			case TO:
246 				i += dimTo(txt + i, &gpos, &our, graph);
247 				break;
248 			case LEADSTO:
249 				i += dimLeadsto(txt + i, &gpos, &our,
250 						graph);
251 				break;
252 			case SUM:
253 				i += dimSum(txt + i, &gpos, &our, graph);
254 				break;
255 			case PROD:
256 				i += dimProd(txt + i, &gpos, &our, graph);
257 				break;
258 			case INT:
259 				i += dimInt(txt + i, &gpos, &our, graph);
260 				break;
261 			case OINT:
262 				i += dimOint(txt + i, &gpos, &our, graph);
263 				break;
264 			case INFTY:
265 				strcat(gpos, "oo");
266 				gpos += 2;
267 				our.x += 2;
268 				i += 5;
269 				break;
270 			case RCEIL:
271 				i += dimRceil(txt + i, &gpos, &our, graph);
272 				break;
273 			case LCEIL:
274 				i += dimLceil(txt + i, &gpos, &our, graph);
275 				break;
276 			case RFLOOR:
277 				i += dimRfloor(txt + i, &gpos, &our,
278 					       graph);
279 				break;
280 			case LFLOOR:
281 				i += dimLfloor(txt + i, &gpos, &our,
282 					       graph);
283 				break;
284 			case ESCAPE:
285 				i++;
286 				our.x++;
287 				*gpos = txt[i];
288 				gpos++;
289 				*gpos = 0;
290 				break;
291 			case ERR:
292 			default:
293 				fprintf(stderr,
294 					"I screwed up in dim, this should never happen!\n");
295 				exit(1);
296 				break;
297 			}
298 		}
299 	}
300 	graph->dim = our;
301 	return our;
302 }
303 
304 char           *
PotLineEnd(char * txt)305 PotLineEnd(char *txt)
306 {
307 	int             len = strlen(txt);
308 	int             i,j;
309 	char * plbp = "+-*/=~";
310 	for (i = 0; i < len; i++)
311 	{
312 		/* return pointer to the next potential endline position */
313 		if (strncmp(txt + i, "\\begin", 6) == 0)	/* skip nested parts */
314 			i = 4 + getbegin_endEnd(txt + i + 6) - txt;
315 		else if (strncmp(txt + i, "\\left", 5) == 0)
316 			i = 6 + findClosingLRBrace(txt + i + 5) - txt;
317 		else if (txt[i] == '{')
318 			i = findClosingBrace(txt + i + 1) - txt;
319 		else if (txt[i] == '\\')
320 			i++;
321 		else if (txt[i] == '\n')
322 			return txt + i;
323 		else
324 			for (j=0;j<6;j++)
325 				if(plbp[j]==txt[i])
326 					return txt + i + 1;
327 
328 	}
329 	return txt+i;		/* no potential line end found */
330 }
331 
332 Tdim
eqdim(char * txt,struct Tgraph * graph,int ll)333 eqdim(char *txt, struct Tgraph * graph, int ll)
334 {
335 	/* if the linelength (ll) is zero we do not break, otherwise we try to fit the eq within ll
336 	columns */
337 	if (ll)
338 	{
339 		/* position linebreaks at + - / * = in the top level */
340 		char * END=txt+strlen(txt);
341 		struct Tgraph *dummy = malloc(sizeof(struct Tgraph));
342 		Tdim dumdim;
343 		char * start=txt;
344 		char * end, c;
345 		char * prevplb=NULL;
346 		int x=0;
347 		while(start<END)
348 		{
349 			end=PotLineEnd(start);
350 			if (SYNTAX_ERR_FLAG==S_ERR)
351 				return dumdim;
352 			c=*end;
353 			*end='\0';
354 			InitGraph(dummy);
355 			dumdim=dim(start,dummy);
356 			if (SYNTAX_ERR_FLAG==S_ERR)
357 				return dumdim;
358 			*end=c;
359 			dealloc(dummy);
360 			x+=dumdim.x;
361 			if(dumdim.x>ll)
362 				SyntaxWarning("Warning: overfull line\n");
363 
364 			if((x>ll)&&(prevplb))
365 			{
366 				/* at the previous potential line end we have to place one */
367 				/* in order to make it easy we create a single char line end, \n */
368 				/* note that enters are removed in preparse to allow multi line editing
369 				*/
370 				*(prevplb)='\n';
371 				x=dumdim.x;
372 			}
373 			if(c=='\n')
374 			{
375 				/* the current end is already a break */
376 				/* reset x, prevent a new linebreak */
377 				prevplb=NULL;
378 				x=0;
379 				end++;
380 			}
381 			else
382 				prevplb=end;
383 
384 			start=end;
385 		}
386 		free(dummy);
387 	}
388 	return dim(txt, graph);
389 }
390