Reset parser in grace_set_project().
[grace.git] / src / typeset.c
blob9d4ba9e1b42fab13a1ceba4d0db67c6008198996
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1996-2002 Grace Development Team
7 *
8 * Maintained by Evgeny Stambulchik
9 *
11 * All Rights Reserved
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 /*
29 * Parsing escape sequences in composite strings
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
36 #include "utils.h"
37 #include "core_utils.h"
38 #include "files.h"
40 int init_font_db(Canvas *canvas)
42 int i, nfonts;
43 char buf[GR_MAXPATHLEN], abuf[GR_MAXPATHLEN], fbuf[GR_MAXPATHLEN], *bufp;
44 FILE *fd;
45 Grace *grace = (Grace *) canvas_get_udata(canvas);
47 /* Set default encoding */
48 bufp = grace_path2(grace, "fonts/enc/", T1_DEFAULT_ENCODING_FILE);
49 if (canvas_set_encoding(canvas, bufp) != RETURN_SUCCESS) {
50 bufp = grace_path2(grace, "fonts/enc/", T1_FALLBACK_ENCODING_FILE);
51 if (canvas_set_encoding(canvas, bufp) != RETURN_SUCCESS) {
52 return RETURN_FAILURE;
56 /* Open & process the font database */
57 fd = grace_openr(grace, "fonts/FontDataBase", SOURCE_DISK);
58 if (fd == NULL) {
59 return RETURN_FAILURE;
62 /* the first line - number of fonts */
63 grace_fgets(buf, GR_MAXPATHLEN - 1, fd);
64 if (sscanf(buf, "%d", &nfonts) != 1 || nfonts <= 0) {
65 fclose(fd);
66 return RETURN_FAILURE;
69 for (i = 0; i < nfonts; i++) {
70 grace_fgets(buf, GR_MAXPATHLEN - 1, fd);
71 if (sscanf(buf, "%s %*s %s", abuf, fbuf) != 2) {
72 fclose(fd);
73 return RETURN_FAILURE;
75 bufp = grace_path2(grace, "fonts/type1/", fbuf);
76 if (canvas_add_font(canvas, bufp, abuf) != RETURN_SUCCESS) {
77 fclose(fd);
78 return RETURN_FAILURE;
81 fclose(fd);
83 return RETURN_SUCCESS;
86 /* TODO: optimize, e.g. via a hashed array */
87 int fmap_proc(const Canvas *canvas, int font_id)
89 Grace *grace = (Grace *) canvas_get_udata(canvas);
90 Project *pr = project_get_data(grace->project);
91 int font = BAD_FONT_ID;
92 unsigned int i;
94 for (i = 0; i < pr->nfonts; i++) {
95 Fontdef *f = &pr->fontmap[i];
97 if (f->id == font_id) {
98 font = canvas_get_font_by_name(canvas, f->fontname);
99 if (font == BAD_FONT_ID) {
100 font = canvas_get_font_by_name(canvas, f->fallback);
103 if (font == BAD_FONT_ID) {
104 char buf[64];
105 sprintf(buf, "Couldn't map font %d to any existing one", f->id);
106 errmsg(buf);
108 font = 0;
111 break;
115 return font;
118 static const TextMatrix unit_tm = UNIT_TM;
120 static int get_escape_args(const char *s, char *buf)
122 int i = 0;
124 if (*s == '{') {
125 s++;
126 while (*s != '\0') {
127 if (*s == '}') {
128 *buf = '\0';
129 return i;
130 } else {
131 *buf = *s;
132 buf++; s++; i++;
137 return -1;
140 static char *expand_macros(const Canvas *canvas, const char *s)
142 Grace *grace = (Grace *) canvas_get_udata(canvas);
143 char *es, *macro, *subst;
144 int i, j, k, slen, extra_len = 0;
146 if (!s) {
147 return NULL;
150 slen = strlen(s);
151 macro = xmalloc(slen*SIZEOF_CHAR);
152 if (!macro) {
153 return NULL;
156 i = 0;
157 while (i < slen) {
158 if (s[i] == '\\' && s[i + 1] == '$' &&
159 (k = get_escape_args(&(s[i + 2]), macro)) >= 0) {
160 if (!strcmp(macro, "timestamp")) {
161 subst = project_get_timestamp(grace->project);
162 } else
163 if (!strcmp(macro, "filename")) {
164 subst = project_get_docname(grace->project);
165 } else
166 if (!strcmp(macro, "filebname")) {
167 subst = get_docbname(grace->project);
168 } else {
169 subst = "";
171 /* 4 == strlen("\\${}") */
172 extra_len += (strlen(subst) - (4 + k));
173 i += (4 + k);
174 } else {
175 i++;
179 es = xmalloc((slen + extra_len + 1)*SIZEOF_CHAR);
180 if (!es) {
181 xfree(macro);
182 return NULL;
185 i = 0; j = 0;
186 while (i < slen) {
187 if (s[i] == '\\' && s[i + 1] == '$' &&
188 (k = get_escape_args(&(s[i + 2]), macro)) >= 0) {
189 if (!strcmp(macro, "timestamp")) {
190 subst = project_get_timestamp(grace->project);
191 } else
192 if (!strcmp(macro, "filename")) {
193 subst = project_get_docname(grace->project);
194 } else
195 if (!strcmp(macro, "filebname")) {
196 subst = get_docbname(grace->project);
197 } else {
198 subst = "";
200 strcpy(&es[j], subst);
201 i += (4 + k); j += strlen(subst);
202 } else {
203 es[j] = s[i];
204 i++; j++;
207 es[j] = '\0';
209 xfree(macro);
211 return es;
214 int csparse_proc(const Canvas *canvas, const char *s, CompositeString *cstring)
216 Grace *grace = (Grace *) canvas_get_udata(canvas);
217 CStringSegment *cseg;
219 char *string, *ss, *buf, *acc_buf;
220 int inside_escape = FALSE;
221 int i, isub, j;
222 int acc_len;
223 int slen;
224 char ccode;
225 int upperset = FALSE;
226 double scale;
227 TextMatrix tm_buf;
229 int font = BAD_FONT_ID, new_font = font;
230 int color = BAD_COLOR, new_color = color;
231 TextMatrix tm = unit_tm, tm_new = tm;
232 double hshift = 0.0, new_hshift = hshift;
233 double baseline = 0.0, baseline_old;
234 double vshift = baseline, new_vshift = vshift;
235 int underline = FALSE, overline = FALSE;
236 int new_underline = underline, new_overline = overline;
237 int kerning = FALSE, new_kerning = kerning;
238 int direction = STRING_DIRECTION_LR, new_direction = direction;
239 int advancing = TEXT_ADVANCING_LR, new_advancing = advancing;
240 int ligatures = FALSE, new_ligatures = ligatures;
242 int setmark = MARK_NONE;
243 int gotomark = MARK_NONE, new_gotomark = gotomark;
245 double val;
247 string = expand_macros(canvas, s);
249 if (string == NULL) {
250 return RETURN_FAILURE;
253 slen = strlen(string);
255 if (slen == 0) {
256 return RETURN_FAILURE;
259 ss = xmalloc(slen + 1);
260 buf = xmalloc(slen + 1);
261 acc_buf = xmalloc(slen + 1);
262 if (ss == NULL || buf == NULL || acc_buf == NULL) {
263 xfree(acc_buf);
264 xfree(buf);
265 xfree(ss);
266 xfree(string);
267 return RETURN_FAILURE;
270 isub = 0;
271 ss[isub] = 0;
273 for (i = 0; i <= slen; i++) {
274 ccode = string[i];
275 acc_len = 0;
276 if (ccode < 32 && ccode > 0) {
277 /* skip control codes */
278 continue;
280 if (inside_escape) {
281 inside_escape = FALSE;
283 if (isdigit(ccode)) {
284 new_font = ccode - '0';
285 continue;
286 } else if (ccode == 'd') {
287 i++;
288 switch (string[i]) {
289 case 'l':
290 new_direction = STRING_DIRECTION_LR;
291 break;
292 case 'r':
293 new_direction = STRING_DIRECTION_RL;
294 break;
295 case 'L':
296 new_advancing = TEXT_ADVANCING_LR;
297 break;
298 case 'R':
299 new_advancing = TEXT_ADVANCING_RL;
300 break;
301 default:
302 /* undo advancing */
303 i--;
304 break;
306 continue;
307 } else if (ccode == 'F') {
308 i++;
309 switch (string[i]) {
310 case 'k':
311 new_kerning = TRUE;
312 break;
313 case 'K':
314 new_kerning = FALSE;
315 break;
316 case 'l':
317 new_ligatures = TRUE;
318 break;
319 case 'L':
320 new_ligatures = FALSE;
321 break;
322 default:
323 /* undo advancing */
324 i--;
325 break;
327 continue;
328 } else if (isoneof(ccode, "cCsSNBxuUoO+-qQn")) {
329 switch (ccode) {
330 case 's':
331 new_vshift -= tm_size(&tm_new)*SUBSCRIPT_SHIFT;
332 tm_scale(&tm_new, SSCRIPT_SCALE);
333 break;
334 case 'S':
335 new_vshift += tm_size(&tm_new)*SUPSCRIPT_SHIFT;
336 tm_scale(&tm_new, SSCRIPT_SCALE);
337 break;
338 case 'N':
339 scale = 1.0/tm_size(&tm_new);
340 tm_scale(&tm_new, scale);
341 new_vshift = baseline;
342 break;
343 case 'B':
344 new_font = BAD_FONT_ID;
345 break;
346 case 'x':
347 new_font = get_font_by_name(grace->project, "Symbol");
348 break;
349 case 'c':
350 upperset = TRUE;
351 break;
352 case 'C':
353 upperset = FALSE;
354 break;
355 case 'u':
356 new_underline = TRUE;
357 break;
358 case 'U':
359 new_underline = FALSE;
360 break;
361 case 'o':
362 new_overline = TRUE;
363 break;
364 case 'O':
365 new_overline = FALSE;
366 break;
367 case '-':
368 tm_scale(&tm_new, 1.0/ENLARGE_SCALE);
369 break;
370 case '+':
371 tm_scale(&tm_new, ENLARGE_SCALE);
372 break;
373 case 'q':
374 tm_slant(&tm_new, OBLIQUE_FACTOR);
375 break;
376 case 'Q':
377 tm_slant(&tm_new, -OBLIQUE_FACTOR);
378 break;
379 case 'n':
380 new_gotomark = MARK_CR;
381 baseline -= 1.0;
382 new_vshift = baseline;
383 new_hshift = 0.0;
384 break;
386 continue;
387 } else if (isoneof(ccode, "fhvVzZmM#rltTR") &&
388 (j = get_escape_args(&(string[i + 1]), buf)) >= 0) {
389 i += (j + 2);
390 switch (ccode) {
391 case 'f':
392 if (j == 0) {
393 new_font = BAD_FONT_ID;
394 } else if (isdigit(buf[0])) {
395 new_font = atoi(buf);
396 } else {
397 new_font = get_font_by_name(grace->project, buf);
399 break;
400 case 'v':
401 if (j == 0) {
402 new_vshift = baseline;
403 } else {
404 val = atof(buf);
405 new_vshift += tm_size(&tm_new)*val;
407 break;
408 case 'V':
409 baseline_old = baseline;
410 if (j == 0) {
411 baseline = 0.0;
412 } else {
413 val = atof(buf);
414 baseline += tm_size(&tm_new)*val;
416 new_vshift = baseline;
417 break;
418 case 'h':
419 val = atof(buf);
420 new_hshift = tm_size(&tm_new)*val;
421 break;
422 case 'z':
423 if (j == 0) {
424 scale = 1.0/tm_size(&tm_new);
425 tm_scale(&tm_new, scale);
426 } else {
427 scale = atof(buf);
428 tm_scale(&tm_new, scale);
430 break;
431 case 'Z':
432 scale = atof(buf)/tm_size(&tm_new);
433 tm_scale(&tm_new, scale);
434 break;
435 case 'r':
436 tm_rotate(&tm_new, atof(buf));
437 break;
438 case 'l':
439 tm_slant(&tm_new, atof(buf));
440 break;
441 case 't':
442 if (j == 0) {
443 tm_new = unit_tm;
444 } else {
445 if (sscanf(buf, "%lf %lf %lf %lf",
446 &tm_buf.cxx, &tm_buf.cxy,
447 &tm_buf.cyx, &tm_buf.cyy) == 4) {
448 tm_product(&tm_new, &tm_buf);
451 break;
452 case 'T':
453 if (sscanf(buf, "%lf %lf %lf %lf",
454 &tm_buf.cxx, &tm_buf.cxy,
455 &tm_buf.cyx, &tm_buf.cyy) == 4) {
456 tm_new = tm_buf;
458 break;
459 case 'm':
460 setmark = atoi(buf);
461 break;
462 case 'M':
463 new_gotomark = atoi(buf);
464 new_vshift = baseline;
465 new_hshift = 0.0;
466 break;
467 case 'R':
468 if (j == 0) {
469 new_color = BAD_COLOR;
470 } else if (isdigit(buf[0])) {
471 new_color = atof(buf);
472 } else {
473 new_color = get_color_by_name(grace->project, buf);
475 break;
476 case '#':
477 if (j % 2 == 0) {
478 int k;
479 char hex[3];
480 hex[2] = '\0';
481 for (k = 0; k < j; k += 2) {
482 hex[0] = buf[k];
483 hex[1] = buf[k + 1];
484 acc_buf[acc_len] = strtol(hex, NULL, 16);
485 acc_len++;
488 break;
491 if (ccode != '#') {
492 continue;
494 } else {
495 /* store the char */
496 acc_buf[0] = (ccode + (upperset*0x80)) & 0xff;
497 acc_len = 1;
499 } else {
500 if (ccode == '\\') {
501 inside_escape = TRUE;
502 continue;
503 } else {
504 /* store the char */
505 acc_buf[0] = (ccode + (upperset*0x80)) & 0xff;
506 acc_len = 1;
510 if ((new_font != font ) ||
511 (new_color != color ) ||
512 (tm_new.cxx != tm.cxx ) ||
513 (tm_new.cxy != tm.cxy ) ||
514 (tm_new.cyx != tm.cyx ) ||
515 (tm_new.cyy != tm.cyy ) ||
516 (new_hshift != 0.0 ) ||
517 (new_vshift != vshift ) ||
518 (new_underline != underline ) ||
519 (new_overline != overline ) ||
520 (new_kerning != kerning ) ||
521 (new_direction != direction ) ||
522 (new_advancing != advancing ) ||
523 (new_ligatures != ligatures ) ||
524 (setmark >= 0 ) ||
525 (new_gotomark >= 0 ) ||
526 (ccode == 0 )) {
528 if (isub != 0 || setmark >= 0) { /* non-empty substring */
530 cseg = cstring_seg_new(cstring);
531 cseg->font = font;
532 cseg->color = color;
533 cseg->tm = tm;
534 cseg->hshift = hshift;
535 cseg->vshift = vshift;
536 cseg->underline = underline;
537 cseg->overline = overline;
538 cseg->kerning = kerning;
539 cseg->direction = direction;
540 cseg->advancing = advancing;
541 cseg->ligatures = ligatures;
542 cseg->setmark = setmark;
543 setmark = MARK_NONE;
544 cseg->gotomark = gotomark;
546 cseg->s = xmalloc(isub*SIZEOF_CHAR);
547 memcpy(cseg->s, ss, isub);
548 cseg->len = isub;
549 isub = 0;
552 font = new_font;
553 color = new_color;
554 tm = tm_new;
555 hshift = new_hshift;
556 if (hshift != 0.0) {
557 /* once a substring is manually advanced, all the following
558 * substrings will be advanced as well!
560 new_hshift = 0.0;
562 vshift = new_vshift;
563 underline = new_underline;
564 overline = new_overline;
565 kerning = new_kerning;
566 direction = new_direction;
567 advancing = new_advancing;
568 ligatures = new_ligatures;
569 gotomark = new_gotomark;
570 if (gotomark >= 0) {
571 /* once a substring is manually advanced, all the following
572 * substrings will be advanced as well!
574 new_gotomark = MARK_NONE;
577 memcpy(&ss[isub], acc_buf, acc_len*SIZEOF_CHAR);
578 isub += acc_len;
581 xfree(acc_buf);
582 xfree(buf);
583 xfree(ss);
584 xfree(string);
586 return RETURN_SUCCESS;