1 /*
2 * FIG : Facility for Interactive Generation of figures
3 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4 * Parts Copyright (c) 1989-2015 by Brian V. Smith
5 * Parts Copyright (c) 1991 by Paul King
6 * Parts Copyright (c) 2016-2020 by Thomas Loimer
7 *
8 * Any party obtaining a copy of these files is granted, free of charge, a
9 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10 * nonexclusive right and license to deal in this software and documentation
11 * files (the "Software"), including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13 * the Software, and to permit persons who receive copies from any such
14 * party to do so, with the only requirement being that the above copyright
15 * and this permission notice remain intact.
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "f_read.h"
23
24 #include <ctype.h> /* isdigit() */
25 #include <errno.h>
26 #include <limits.h> /* PATH_MAX */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #ifdef HAVE_STRINGS_H
31 #include <strings.h>
32 #endif
33 #ifdef I18N
34 #include <locale.h>
35 #endif
36
37 #include "resources.h"
38 #include "object.h"
39 #include "mode.h"
40
41 #include "d_spline.h"
42 #include "e_update.h"
43 #include "f_picobj.h"
44 #include "f_util.h"
45 #include "u_bound.h"
46 #include "u_create.h"
47 #include "u_fonts.h"
48 #include "u_free.h"
49 #include "u_scale.h"
50 #include "u_translate.h"
51 #include "w_canvas.h"
52 #include "w_color.h"
53 #include "w_drawprim.h"
54 #include "w_file.h"
55 #include "w_layers.h"
56 #include "w_msgpanel.h"
57 #include "w_setup.h"
58 #include "w_util.h"
59 #include "w_zoom.h"
60
61 #include "xfig_math.h"
62
63 extern int read_1_3_objects(FILE *fp, char *buf, F_compound *obj,
64 int *resolution); /* f_readold.c */
65
66 /* EXPORTS */
67
68 int defer_update_layers = 0; /* if != 0, update_layers() doesn't update */
69 void fix_angle (float *angle);
70 int line_no, save_line; /* current input line number */
71 int num_object; /* current number of objects */
72 char *read_file_name; /* current input file name */
73 void swap_colors (void);
74
75 /* LOCAL */
76
77 static char Err_incomp[] = "Incomplete %s object at line %d.";
78
79 static void read_colordef(void);
80 static F_ellipse *read_ellipseobject(void);
81 static F_line *read_lineobject(FILE *fp);
82 static F_text *read_textobject(FILE *fp);
83 static F_spline *read_splineobject(FILE *fp);
84 static F_arc *read_arcobject(FILE *fp);
85 static F_compound *read_compoundobject(FILE *fp);
86 static int save_comment(void);
87 static char *attach_comments(void);
88 static void count_lines_correctly(FILE *fp);
89 static int read_return(int status);
90 static Boolean contains_picture(F_compound *compound);
91
92 #define FILL_CONVERT(f) \
93 ((proto >= 22) ? (f): \
94 (((proto>=20) || (f) == 0 || !TFX) ? \
95 (f-1) : (!TFX? (NUMFILLPATS-1) - ((f)-1)*5: UNFILLED)))
96
97
98 /* max number of comments that can be stored with each object */
99 #define MAXCOMMENTS 100
100
101 /* input buffer length */
102 #define BUF_SIZE 1024
103
104 static char buf[BUF_SIZE]; /* input buffer */
105 static char *comments[MAXCOMMENTS]; /* comments saved for current object */
106 static int numcom; /* current comment index */
107 static Boolean com_alloc = False; /* whether or not the comment array
108 has been initialized */
109 static int TFX; /* true for 1.4TFX protocol */
110 static int proto; /* file protocol*10 */
111 static float fproto, xfigproto; /* floating values for protocol of
112 figure file and current protocol */
113
114 /* initialize the user color counter - then read figure file.
115 Called from load_file(), merge_file(), preview_figure(), load_lib_obj(),
116 and paste(), but NOT from read_figure() (import Fig as picture) */
117
118 static void merge_colors (F_compound *objects);
119 static int readfp_fig (FILE *fp, F_compound *obj, Boolean merge, int xoff,
120 int yoff, fig_settings *settings);
121 static int read_line (FILE *fp);
122 static int read_objects (FILE *fp, F_compound *obj, int *res);
123 static void scale_figure (F_compound *obj, float mul, int offset);
124 static void shift_figure (F_compound *obj);
125 static void fix_depth (int *depth);
126 static void check_color (int *color);
127 static void convert_arrow (int *type, float *wd, float *ht);
128 static void skip_line (FILE *fp);
129 static int backslash_count (char *cp, int start);
130 static void renumber_comp (F_compound *compound);
131 static void renumber (int *color);
132
133 int
read_figc(char * file_name,F_compound * obj,Boolean merge,Boolean remapimages,int xoff,int yoff,fig_settings * settings)134 read_figc(char *file_name, F_compound *obj, Boolean merge, Boolean remapimages,
135 int xoff, int yoff, fig_settings *settings)
136 {
137 int i,status;
138
139 n_num_usr_cols = -1;
140 for (i=0; i<MAX_USR_COLS; i++)
141 n_colorFree[i] = True;
142
143 /* now read the file */
144 status = read_fig(file_name, obj, merge, xoff, yoff, settings);
145
146 if (status != 0)
147 return status;
148
149 if (merge) {
150 /* merge any user colors from the new file just read into the current figure */
151 merge_colors(obj);
152 } else {
153 /* now swap old figure colors with new colors (if any) */
154 swap_colors();
155 current_memory = -1;
156 show_pencolor();
157 show_fillcolor();
158 }
159
160 /* and remap colors for all pictures */
161 /* but only if a picture object is in the new file */
162 if (remapimages && contains_picture(obj))
163 remap_imagecolors();
164
165 return status;
166 }
167
168 static Boolean
contains_picture(F_compound * compound)169 contains_picture(F_compound *compound)
170 {
171 F_line *l;
172 F_compound *c;
173 /* traverse the compounds in this compound */
174 for (c = compound->compounds; c != NULL; c = c->next) {
175 if (contains_picture(c))
176 return True;
177 }
178 for (l = compound->lines; l != NULL; l = l->next) {
179 if (l->type == T_PICTURE)
180 return True;
181 }
182 return False;
183 }
184
185 /**********************************************************
186 Read_fig returns :
187
188 0 : successful read.
189 BAD_FORMAT : File is in incorrect format
190 EMPTY_FILE : File is empty
191 NO_VERSION : No version was found in "#FIG" header
192 err_no : if file can not be read for various reasons
193 (from /usr/include/sys/errno.h)
194
195 The resolution (ppi) is stored in resolution.
196 The coordinate system is 1 for lower left at 0,0 and
197 2 for upper left at 0,0, but this value is not used,
198 because xfig only uses 2 for the coordinate system.
199
200 If "merge" is True, the user colors are merged into the color
201 set, else they replace the current user colors.
202
203 Also, if merging, if the file being merged into the main figure
204 has different units (inches/cm) it is rescaled to match the
205 main figure.
206
207 If update_figs is false, any imported images are read in, otherwise
208 they are not. The latter case is used for the -update command-line
209 option where the user just wants to read and re-write Fig files
210 to bring them up-to-date.
211 **********************************************************/
212
213 int
read_fig(char * file_name,F_compound * obj,Boolean merge,int xoff,int yoff,fig_settings * settings)214 read_fig(char *file_name, F_compound *obj, Boolean merge, int xoff, int yoff, fig_settings *settings)
215 {
216 FILE *fp;
217 int status;
218
219 read_file_name = file_name;
220 first_file_msg = True;
221 if (uncompress_file(file_name) == False)
222 return ENOENT; /* doesn't exist */
223 if ((fp = fopen(file_name, "r")) == NULL)
224 return errno;
225 else {
226 if (!update_figs)
227 put_msg("Reading objects from \"%s\" ...", file_name);
228 #ifdef I18N
229 /* set the numeric locale to C so we get decimal points for numbers */
230 setlocale(LC_NUMERIC, "C");
231 #endif /* I18N */
232 status = readfp_fig(fp, obj, merge, xoff, yoff, settings);
233 #ifdef I18N
234 /* reset to original locale */
235 setlocale(LC_NUMERIC, "");
236 #endif /* I18N */
237 fclose(fp);
238 /* so subsequent file_msg() calls don't print wrong file name */
239 first_file_msg = False;
240 return status;
241 }
242 }
243
244 static int
readfp_fig(FILE * fp,F_compound * obj,Boolean merge,int xoff,int yoff,fig_settings * settings)245 readfp_fig(FILE *fp, F_compound *obj, Boolean merge, int xoff, int yoff, fig_settings *settings)
246 {
247 int status;
248 int i;
249 int resolution;
250 char versstring[10];
251
252 defer_update_layers = 1; /* prevent update_layers() from updating */
253
254 /* initialize settings structure in case we read an older Fig format */
255 settings->landscape = appres.landscape;
256 settings->flushleft = appres.flushleft;
257 settings->units = appres.INCHES;
258 settings->papersize = appres.papersize;
259 settings->magnification = appres.magnification;
260 settings->multiple = appres.multiple;
261 settings->transparent = appres.transparent;
262
263 num_object = 0;
264 /* reset comment number */
265 numcom = 0;
266 /* initialize the comment array */
267 if (!com_alloc)
268 for (i=0; i<MAXCOMMENTS; i++)
269 comments[i] = (char *) NULL;
270 com_alloc = True;
271 memset(obj, 0, COMOBJ_SIZE);
272 line_no = 1;
273 /* read the version header line (e.g. #FIG 3.2) */
274 if (fgets(buf, BUF_SIZE, fp) == 0)
275 return read_return(EMPTY_FILE);
276 if (strncmp(buf, "#FIG", 4) == 0) { /* versions 1.4/later have #FIG in first line */
277 if (strlen(buf) <= 6) {
278 return read_return(NO_VERSION); /* Short line - say corrupt */
279 }
280 if ((sscanf((char*)(strchr(buf, ' ') + 1), "%f", &fproto)) == 0) /* assume 1.4 */
281 proto = 14;
282 else
283 proto = (fproto + .01) * 10; /* protocol version*10 */
284
285 /* if file protocol != current protocol, give message */
286 strcpy(versstring, PROTOCOL_VERSION); /* copy because gcc doesn't allow writing */
287 sscanf(versstring,"%f",&xfigproto); /* to const strings. sscanf does ungetc */
288 if (fproto > xfigproto) {
289 file_msg("You must have a NEWER version of Xfig to load this figure (%.1f).",
290 fproto);
291 return read_return(BAD_FORMAT);
292 }
293 /* Protocol 2.2 was only beta test - 3.0 is the release (and is identical) */
294 if (proto == 22)
295 proto = 30;
296
297 TFX = False;
298 if (strstr(buf, "TFX") != NULL)
299 TFX = True;
300
301 if (proto >= 30) {
302 /* read Portrait/Landscape indicator now */
303 if (read_line(fp) < 0) {
304 file_msg("No Portrait/Landscape specification");
305 return read_return(BAD_FORMAT); /* error */
306 }
307 settings->landscape = (strncasecmp(buf,"landscape",9) == 0);
308
309 /* read Centering indicator now */
310 if (read_line(fp) < 0) {
311 file_msg("No Center/Flushleft specification");
312 return read_return(BAD_FORMAT); /* error */
313 }
314 if ((strncasecmp(buf,"center",6) == 0) ||
315 (strncasecmp(buf,"flush",5) == 0)) {
316 /* use negative to ensure 1/0 (strcmp may return 3 or 4 for false) */
317 settings->flushleft = !strncasecmp(buf,"flush",5);
318 /* NOW read metric/inches indicator */
319 if (read_line(fp) < 0) {
320 file_msg("No Metric/Inches specification");
321 return read_return(BAD_FORMAT); /* error */
322 }
323 }
324 /* set metric/inches mode appropriately */
325 settings->units = (strncasecmp(buf,"metric",5) != 0);
326
327 /* paper size, magnification, multiple page flag and transparent color
328 (for GIF export) new in 3.2 */
329 if (proto >= 32) {
330 /* read paper size now */
331 if (read_line(fp) < 0) {
332 file_msg("No Paper size specification");
333 return read_return(BAD_FORMAT); /* error */
334 }
335 /* parse the paper size */
336 settings->papersize = parse_papersize(buf);
337
338 /* read magnification now */
339 if (read_line(fp) < 0) {
340 file_msg("No Magnification specification");
341 return read_return(BAD_FORMAT); /* error */
342 }
343 settings->magnification = atoi(buf);
344 if (settings->magnification <= 0.) {
345 file_msg("Negative Magnification is not supported");
346 return read_return(BAD_FORMAT);
347 }
348
349 /* read multiple page flag now */
350 if (read_line(fp) < 0) {
351 file_msg("No Multiple page flag specification");
352 return read_return(BAD_FORMAT); /* error */
353 }
354 if (strncasecmp(buf,"multiple",8) != 0 &&
355 strncasecmp(buf,"single",6) != 0) {
356 file_msg("No Multiple page flag specification");
357 return read_return(BAD_FORMAT);
358 }
359 settings->multiple = (strncasecmp(buf,"multiple",8) == 0);
360
361 /* read transparent color now */
362 if (read_line(fp) < 0) {
363 file_msg("No Transparent color specification");
364 return read_return(BAD_FORMAT); /* error */
365 }
366 settings->transparent = atoi(buf);
367 if (settings->transparent < CANVAS_BG) { /* see object.h */
368 file_msg("Invalid transparent color specification");
369 return read_return(BAD_FORMAT);
370 }
371 }
372 }
373 /* now read the figure itself */
374 status = read_objects(fp, obj, &resolution);
375
376 } else {
377 file_msg("Seeing if this figure is Fig format 1.3");
378 file_msg("If this doesn't work then this is not a Fig file.");
379 proto = 13;
380 status = read_1_3_objects(fp, buf, obj, &resolution);
381 }
382 /* don't go any further if there was an error in reading the figure */
383 if (status != 0) {
384 return read_return(status);
385 }
386
387 n_num_usr_cols++; /* number of user colors = max index + 1 */
388 /***************************************************************************
389 The older versions of xfig (1.3 to 2.1) used values that ended in 4 or 9
390 for coordinates on the "grid". When multiplied by 15 for the 3.0
391 resolution these values ended up 14 "new" pixels off the grid.
392
393 For 3.0 files, 1 is added to the coordinates, and in addition, the USER
394 is supposed to set the x and y offset in the file panel both to the
395 amount necessary to correct the problem.
396 For older files 1 is first added to coordinates then they are multiplied
397 by 15.
398 ***************************************************************************/
399 /* See also doc/FORMAT3.0 */
400
401 /* I do not see, where 1 is first added. */
402 if (proto == 30 || proto == 13) {
403 /* Fig 1.3 objects seem to lie on "nice" coordinates. */
404 scale_figure(obj,((float)PIX_PER_INCH)/resolution,0);
405 } else if (resolution != PIX_PER_INCH) {
406 if (proto == 21 && resolution == 76 && !settings->units)
407 scale_figure(obj,((float)PIX_PER_INCH)/80,15); /* for 2.1.8S, HWS */
408 else
409 scale_figure(obj,((float)PIX_PER_INCH)/resolution,15);
410 }
411
412 /* if merging a figure with different units, rescale for mixed units, HWS */
413 if (merge && (proto >= 30)) {
414 if (!appres.INCHES && settings->units)
415 read_scale_compound(obj,(2.54*PIX_PER_CM)/((float)PIX_PER_INCH),0);
416 if (appres.INCHES && !settings->units)
417 read_scale_compound(obj,((float)PIX_PER_INCH)/(2.54*PIX_PER_CM),0);
418 }
419 /* if the user wants to scale the figure */
420 if (scale_factor != 1.0)
421 read_scale_compound(obj, scale_factor, 0);
422
423 /* shift the figure by the amount in the x and y offsets from the file panel */
424 translate_compound(obj, xoff, yoff);
425
426 /* get bounding box of whole figure */
427 compound_bound(obj,&obj->nwcorner.x,&obj->nwcorner.y,&obj->secorner.x,&obj->secorner.y);
428
429 /* ask the user if the figure should be shifted
430 if there are negative coords */
431 if (!update_figs) {
432 shift_figure(obj);
433
434 /* now update the grid/ruler units */
435 if (settings->units) {
436 /* inches */
437 if (strcasecmp(appres.tgrid_unit, "default") != 0) {
438 /* the user specified a units when he started, try to keep it */
439 if (strcasecmp(appres.tgrid_unit, "tenth") == 0 ||
440 strcasecmp(appres.tgrid_unit, "ten") == 0 ||
441 strcasecmp(appres.tgrid_unit, "1/10") == 0 ||
442 strcasecmp(appres.tgrid_unit, "10") == 0)
443 settings->grid_unit = TENTH_UNIT;
444 else
445 settings->grid_unit = FRACT_UNIT;
446 } else {
447 settings->grid_unit = FRACT_UNIT;
448 }
449 } else {
450 /* metric, only choice */
451 settings->grid_unit = MM_UNIT;
452 }
453 } /* !update_figs */
454
455 /* return with status */
456 return read_return(status);
457 }
458
459 /* clear defer_update_layers counter, update the layer buttons and return status */
460
461 static int
read_return(int status)462 read_return(int status)
463 {
464 defer_update_layers = 0;
465 if (!update_figs)
466 update_layers();
467 return status;
468 }
469
470 static int
read_objects(FILE * fp,F_compound * obj,int * res)471 read_objects(FILE *fp, F_compound *obj, int *res)
472 {
473 F_ellipse *e, *le = NULL;
474 F_line *l, *ll = NULL;
475 F_text *t, *lt = NULL;
476 F_spline *s, *ls = NULL;
477 F_arc *a, *la = NULL;
478 F_compound *c, *lc = NULL;
479 int object, ppi, coord_sys;
480
481 if (read_line(fp) < 0) {
482 file_msg("No Resolution specification; figure is empty");
483 return BAD_FORMAT;
484 }
485
486 /* read the resolution (ppi) and the coordinate system used (upper-left or lower-left) */
487 if (sscanf(buf, "%d%d\n", &ppi, &coord_sys) != 2) {
488 file_msg("Figure resolution or coordinate specifier missing in line %d.", line_no);
489 return BAD_FORMAT;
490 }
491
492 if (ppi <= 0.) {
493 file_msg("Negative figure resolution (%g) is not supported in line %d.",
494 ppi, line_no);
495 return BAD_FORMAT;
496 }
497
498 /* attach any comments found thus far to the whole figure */
499 obj->comments = attach_comments();
500
501 /* save the resolution for caller */
502 *res = ppi;
503
504 while (read_line(fp) > 0) {
505 if (sscanf(buf, "%d", &object) != 1) {
506 file_msg("Incorrect format at line %d.", line_no);
507 return (num_object != 0? 0: BAD_FORMAT); /* ok if any objects have been read */
508 }
509 switch (object) {
510 case O_COLOR_DEF:
511 read_colordef();
512 if (num_object) {
513 file_msg("Color definitions must come before other objects (line %d).",
514 line_no);
515 }
516 break;
517 case O_POLYLINE:
518 if ((l = read_lineobject(fp)) == NULL)
519 continue;
520 if (ll)
521 ll = (ll->next = l);
522 else
523 ll = obj->lines = l;
524 num_object++;
525 break;
526 case O_SPLINE:
527 if ((s = read_splineobject(fp)) == NULL)
528 continue;
529 if (ls)
530 ls = (ls->next = s);
531 else
532 ls = obj->splines = s;
533 num_object++;
534 break;
535 case O_ELLIPSE:
536 if ((e = read_ellipseobject()) == NULL)
537 continue;
538 if (le)
539 le = (le->next = e);
540 else
541 le = obj->ellipses = e;
542 num_object++;
543 break;
544 case O_ARC:
545 if ((a = read_arcobject(fp)) == NULL)
546 continue;
547 if (la)
548 la = (la->next = a);
549 else
550 la = obj->arcs = a;
551 num_object++;
552 break;
553 case O_TXT:
554 if ((t = read_textobject(fp)) == NULL)
555 continue;
556 if (lt)
557 lt = (lt->next = t);
558 else
559 lt = obj->texts = t;
560 num_object++;
561 break;
562 case O_COMPOUND:
563 if ((c = read_compoundobject(fp)) == NULL)
564 continue;
565 if (lc)
566 lc = (lc->next = c);
567 else
568 lc = obj->compounds = c;
569 num_object++;
570 break;
571 default:
572 file_msg("Incorrect object code at line %d.", line_no);
573 continue;
574 } /* switch */
575
576 } /* while */
577
578 if (feof(fp))
579 return 0;
580 else
581 return errno;
582 } /* read_objects */
583
584 int
parse_papersize(char * size)585 parse_papersize(char *size)
586 {
587 int i,len;
588 char *c;
589
590 /* first get rid of trailing newline */
591 if (size[strlen(size)-1]=='\n')
592 size[strlen(size)-1]='\0';
593 /* then truncate at first space or parenthesis "(" in passed size */
594 if (((c=strchr(size,' '))!= NULL)||((c=strchr(size,'(')) != NULL)) {
595 *c ='\0';
596 }
597 len = strlen(size);
598 /* change ledger (deprecated) to tabloid */
599 if (strncasecmp(size,"ledger",len) == 0)
600 strcpy(size,"tabloid");
601 for (i=0; i<NUMPAPERSIZES; i++) {
602 if (strncasecmp(size,paper_sizes[i].sname,len) == 0)
603 break;
604 }
605 /* return entry 0 for bad papersize */
606 if (i >= NUMPAPERSIZES)
607 return 0;
608 return i;
609 }
610
611 static void
read_colordef(void)612 read_colordef(void)
613 {
614 int c,r,g,b;
615
616 if ((sscanf(buf, "%*d %d #%02x%02x%02x", &c, &r, &g, &b) != 4) ||
617 (c < NUM_STD_COLS) || (c >= MAX_USR_COLS+NUM_STD_COLS)) {
618 buf[strlen(buf)-1]='\0'; /* remove the newline */
619 file_msg("Invalid color definition: %s, setting to black (#00000).",buf);
620 r=g=b=0;
621 c = NUM_STD_COLS;
622 }
623 /* make in the range 0...MAX_USR_COLS */
624 c -= NUM_STD_COLS;
625 n_user_colors[c].red = r*256;
626 n_user_colors[c].green = g*256;
627 n_user_colors[c].blue = b*256;
628 n_colorFree[c] = False;
629 /* keep track of highest color number */
630 n_num_usr_cols = max2(c, n_num_usr_cols);
631 }
632
633 static F_arc *
read_arcobject(FILE * fp)634 read_arcobject(FILE *fp)
635 {
636 F_arc *a;
637 int n, fa, ba;
638 int type, style;
639 float thickness, wd, ht;
640
641 if ((a = create_arc()) == NULL){
642 numcom=0;
643 return NULL;
644 }
645
646 save_line = line_no;
647 a->next = NULL;
648 a->for_arrow = a->back_arrow = NULL;
649 if (proto >= 30) {
650 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%f%d%d%d%d%f%f%d%d%d%d%d%d\n",
651 &a->type, &a->style, &a->thickness,
652 &a->pen_color, &a->fill_color, &a->depth,
653 &a->pen_style, &a->fill_style,
654 &a->style_val, &a->cap_style,
655 &a->direction, &fa, &ba,
656 &a->center.x, &a->center.y,
657 &a->point[0].x, &a->point[0].y,
658 &a->point[1].x, &a->point[1].y,
659 &a->point[2].x, &a->point[2].y);
660 } else {
661 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%f%d%d%d%f%f%d%d%d%d%d%d\n",
662 &a->type, &a->style, &a->thickness,
663 &a->pen_color, &a->depth,
664 &a->pen_style, &a->fill_style,
665 &a->style_val, &a->direction, &fa, &ba,
666 &a->center.x, &a->center.y,
667 &a->point[0].x, &a->point[0].y,
668 &a->point[1].x, &a->point[1].y,
669 &a->point[2].x, &a->point[2].y);
670 a->fill_color = a->pen_color;
671 a->cap_style = CAP_BUTT; /* butt line cap */
672 }
673 a->type--; /* internally, 0=open arc, 1=pie wedge */
674 if (((proto < 22) && (n != 19)) || ((proto >= 30) && (n != 21))) {
675 file_msg(Err_incomp, "arc", save_line);
676 free((char *) a);
677 numcom=0;
678 return NULL;
679 }
680 a->fill_style = FILL_CONVERT(a->fill_style);
681 fix_depth(&a->depth);
682 check_color(&a->pen_color);
683 check_color(&a->fill_color);
684 fix_fillstyle(a); /* make sure that black/white have legal fill styles */
685
686 a->comments = attach_comments(); /* attach any comments */
687
688 /* forward arrow */
689 if (fa) {
690 if (read_line(fp) == -1)
691 return a;
692 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
693 file_msg(Err_incomp, "arc", save_line);
694 return a;
695 }
696 /* throw away any arrow heads on pie-wedge arcs */
697 if (a->type == T_OPEN_ARC) {
698 /* make sure arrowhead is legal and convert units */
699 convert_arrow(&type, &wd, &ht);
700 a->for_arrow = new_arrow(type, style, thickness, wd, ht);
701 }
702 }
703
704 /* backward arrow */
705 if (ba) {
706 if (read_line(fp) == -1)
707 return a;
708 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
709 file_msg(Err_incomp, "arc", save_line);
710 return a;
711 }
712 /* throw away any arrow heads on pie-wedge arcs */
713 if (a->type == T_OPEN_ARC) {
714 /* make sure arrowhead is legal and convert units */
715 convert_arrow(&type, &wd, &ht);
716 a->back_arrow = new_arrow(type, style, thickness, wd, ht);
717 }
718 }
719 return a;
720 }
721
722 static F_compound *
read_compoundobject(FILE * fp)723 read_compoundobject(FILE *fp)
724 {
725 F_arc *a, *la = NULL;
726 F_ellipse *e, *le = NULL;
727 F_line *l, *ll = NULL;
728 F_spline *s, *ls = NULL;
729 F_text *t, *lt = NULL;
730 F_compound *com, *c, *lc = NULL;
731 int n, object;
732
733 if ((com = create_compound()) == NULL){
734 numcom=0;
735 return NULL;
736 }
737
738 com->arcs = NULL;
739 com->ellipses = NULL;
740 com->lines = NULL;
741 com->splines = NULL;
742 com->texts = NULL;
743 com->compounds = NULL;
744 com->next = NULL;
745 com->comments = attach_comments(); /* attach any comments */
746
747 save_line = line_no;
748 /* read bounding info for compound */
749 n = sscanf(buf, "%*d%d%d%d%d\n", &com->nwcorner.x, &com->nwcorner.y,
750 &com->secorner.x, &com->secorner.y);
751 /* if compound spec has no bounds, set to 0 and calculate later */
752 if (n <= 0) {
753 com->nwcorner.x = com->nwcorner.y =
754 com->secorner.x = com->secorner.y = 0;
755 } else if (n != 4) {
756 /* otherwise, if there aren't 4 numbers, complain */
757 file_msg(Err_incomp, "compound", save_line);
758 free((char *) com);
759 numcom=0;
760 return NULL;
761 }
762 while (read_line(fp) > 0) {
763 if (sscanf(buf, "%d", &object) != 1) {
764 file_msg(Err_incomp, "compound", save_line);
765 free((char *) com);
766 numcom=0;
767 return NULL;
768 }
769 switch (object) {
770 case O_POLYLINE:
771 if ((l = read_lineobject(fp)) == NULL)
772 continue;
773 if (ll)
774 ll = (ll->next = l);
775 else
776 ll = com->lines = l;
777 break;
778 case O_SPLINE:
779 if ((s = read_splineobject(fp)) == NULL)
780 continue;
781 if (ls)
782 ls = (ls->next = s);
783 else
784 ls = com->splines = s;
785 break;
786 case O_ELLIPSE:
787 if ((e = read_ellipseobject()) == NULL)
788 continue;
789 if (le)
790 le = (le->next = e);
791 else
792 le = com->ellipses = e;
793 break;
794 case O_ARC:
795 if ((a = read_arcobject(fp)) == NULL)
796 continue;
797 if (la)
798 la = (la->next = a);
799 else
800 la = com->arcs = a;
801 break;
802 case O_TXT:
803 if ((t = read_textobject(fp)) == NULL)
804 continue;
805 if (lt)
806 lt = (lt->next = t);
807 else
808 lt = com->texts = t;
809 break;
810 case O_COMPOUND:
811 if ((c = read_compoundobject(fp)) == NULL)
812 continue;
813 if (lc)
814 lc = (lc->next = c);
815 else
816 lc = com->compounds = c;
817 break;
818 case O_END_COMPOUND:
819 /* if compound def had no bounds or all zeroes, calculate bounds now */
820 if (com->nwcorner.x == 0 && com->nwcorner.y == 0 &&
821 com->secorner.x == 0 && com->secorner.y == 0)
822 compound_bound(com, &com->nwcorner.x, &com->nwcorner.y,
823 &com->secorner.x, &com->secorner.y);
824 return com;
825 default:
826 file_msg("Incorrect object code at line %d.", save_line);
827 continue;
828 } /* switch */
829 } /* while (read_line(fp) > 0) */
830
831 if (feof(fp)) {
832 compound_bound(com, &com->nwcorner.x, &com->nwcorner.y,
833 &com->secorner.x, &com->secorner.y);
834 return com;
835 } else {
836 numcom=0;
837 return NULL;
838 }
839 }
840
841 static F_ellipse *
read_ellipseobject(void)842 read_ellipseobject(void)
843 {
844 F_ellipse *e;
845 int n;
846
847 if ((e = create_ellipse()) == NULL){
848 numcom=0;
849 return NULL;
850 }
851
852 save_line = line_no;
853 e->next = NULL;
854 if (proto >= 30) {
855 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%f%d%f%d%d%d%d%d%d%d%d\n",
856 &e->type, &e->style, &e->thickness,
857 &e->pen_color, &e->fill_color, &e->depth,
858 &e->pen_style, &e->fill_style,
859 &e->style_val, &e->direction, &e->angle,
860 &e->center.x, &e->center.y,
861 &e->radiuses.x, &e->radiuses.y,
862 &e->start.x, &e->start.y,
863 &e->end.x, &e->end.y);
864 } else {
865 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%f%d%f%d%d%d%d%d%d%d%d\n",
866 &e->type, &e->style, &e->thickness,
867 &e->pen_color, &e->depth, &e->pen_style, &e->fill_style,
868 &e->style_val, &e->direction, &e->angle,
869 &e->center.x, &e->center.y,
870 &e->radiuses.x, &e->radiuses.y,
871 &e->start.x, &e->start.y,
872 &e->end.x, &e->end.y);
873 e->fill_color = e->pen_color;
874 }
875 if (((proto < 22) && (n != 18)) || ((proto >= 30) && (n != 19))) {
876 file_msg(Err_incomp, "ellipse", save_line);
877 free((char *) e);
878 numcom=0;
879 return NULL;
880 }
881 e->fill_style = FILL_CONVERT(e->fill_style);
882 fix_angle(&e->angle); /* make sure angle is 0 to 2PI */
883 fix_depth(&e->depth);
884 check_color(&e->pen_color);
885 check_color(&e->fill_color);
886 fix_fillstyle(e); /* make sure that black/white have legal fill styles */
887 e->comments = attach_comments(); /* attach any comments */
888 return e;
889 }
890
891 static F_line *
read_lineobject(FILE * fp)892 read_lineobject(FILE *fp)
893 {
894 F_line *l;
895 F_point *p, *q;
896 int n, x, y, fa, ba, npts, cnpts;
897 int type, style, radius_flag;
898 float thickness, wd, ht;
899 int ox, oy;
900 char picfile[PATH_MAX];
901 Boolean dum;
902
903 if ((l = create_line()) == NULL){
904 numcom=0;
905 return NULL;
906 }
907
908 save_line = line_no;
909 l->points = NULL;
910 l->for_arrow = l->back_arrow = NULL;
911 l->next = NULL;
912
913 sscanf(buf, "%*d%d", &l->type);
914
915 /* 2.0 has radius parm only for arc-box objects */
916 /* 2.1 or later has radius parm for all line objects */
917 /* 3.0(experimental 2.2) or later additionally has number of points parm for
918 all line objects and fill color separate from border color */
919 radius_flag = ((proto >= 21) || (l->type == T_ARCBOX && proto == 20));
920 if (proto >= 30) {
921 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%f%d%d%d%d%d%d",
922 &l->type, &l->style, &l->thickness, &l->pen_color, &l->fill_color,
923 &l->depth, &l->pen_style, &l->fill_style, &l->style_val,
924 &l->join_style, &l->cap_style, &l->radius, &fa, &ba, &npts);
925 } else { /* v2.1 and earlier */
926 if (radius_flag) {
927 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%f%d%d%d",
928 &l->type, &l->style, &l->thickness, &l->pen_color, &l->depth,
929 &l->pen_style, &l->fill_style, &l->style_val, &l->radius, &fa, &ba);
930 } else { /* old format uses pen for radius of arc-box * corners */
931 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%f%d%d",
932 &l->type, &l->style, &l->thickness, &l->pen_color,
933 &l->depth, &l->pen_style, &l->fill_style, &l->style_val, &fa, &ba);
934 if (l->type == T_ARCBOX) {
935 l->radius = l->pen_style;
936 l->pen_style = -1;
937 } else
938 l->radius = DEFAULT;
939 }
940 l->fill_color = l->pen_color;
941 l->join_style = JOIN_MITER; /* miter joint */
942 l->cap_style = CAP_BUTT; /* butt line cap */
943 }
944 if ((!radius_flag && n != 10) ||
945 (radius_flag && ((proto == 21 && n != 11) ||
946 ((proto >= 30) && n != 15)))) {
947 file_msg(Err_incomp, "line", save_line);
948 free((char *) l);
949 numcom=0;
950 return NULL;
951 }
952 l->fill_style = FILL_CONVERT(l->fill_style);
953 fix_depth(&l->depth);
954 check_color(&l->pen_color);
955 check_color(&l->fill_color);
956 fix_fillstyle(l); /* make sure that black/white have legal fill styles */
957 /* forward arrow */
958 if (fa) {
959 if (read_line(fp) == -1){
960 numcom=0;
961 return NULL;
962 }
963 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
964 file_msg(Err_incomp, "line", save_line);
965 numcom=0;
966 return NULL;
967 }
968 /* make sure arrowhead is legal and convert units */
969 convert_arrow(&type, &wd, &ht);
970 l->for_arrow = new_arrow(type, style, thickness, wd, ht);
971 }
972 /* backward arrow */
973 if (ba) {
974 if (read_line(fp) == -1){
975 numcom=0;
976 return NULL;
977 }
978 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
979 file_msg(Err_incomp, "line", save_line);
980 numcom=0;
981 return NULL;
982 }
983 /* make sure arrowhead is legal and convert units */
984 convert_arrow(&type, &wd, &ht);
985 l->back_arrow = new_arrow(type, style, thickness, wd, ht);
986 }
987 if (l->type == T_PICTURE) {
988 char s1[PATH_MAX];
989
990 if (read_line(fp) == -1) {
991 free((char *) l);
992 numcom=0;
993 return NULL;
994 }
995 if ((l->pic = create_pic()) == NULL) {
996 free((char *) l);
997 numcom=0;
998 return NULL;
999 }
1000 if (sscanf(buf, "%d %[^\n]", &l->pic->flipped, s1) != 2) {
1001 file_msg(Err_incomp, "Picture Object", save_line);
1002 free((char *) l);
1003 numcom=0;
1004 return NULL;
1005 }
1006
1007 /* if path is relative convert it to absolute path */
1008 if (s1[0] != '/')
1009 sprintf(picfile, "%s/%s", cur_file_dir, s1);
1010 else
1011 strcpy(picfile, s1);
1012
1013 if (!update_figs) {
1014 /* only read in the image if update_figs is False */
1015 read_picobj(l->pic, picfile, l->pen_color, False, &dum);
1016 } else {
1017 /* otherwise just make a pseudo entry with the filename */
1018 l->pic->pic_cache = create_picture_entry();
1019 l->pic->pic_cache->file = strdup(picfile);
1020 }
1021 /* we've read in a pic object - merge_file uses this info to decide
1022 whether or not to remap any picture colors in first figure */
1023 pic_obj_read = True;
1024 } else
1025 l->pic = NULL;
1026
1027 if ((p = create_point()) == NULL) {
1028 free((char *) l);
1029 numcom=0;
1030 return NULL;
1031 }
1032
1033 l->points = p;
1034 p->next = NULL;
1035
1036 /* read first point */
1037 line_no++;
1038 if (fscanf(fp, "%d%d", &p->x, &p->y) != 2) {
1039 file_msg(Err_incomp, "line", save_line);
1040 free_linestorage(l);
1041 numcom=0;
1042 return NULL;
1043 }
1044 ox = p->x;
1045 oy = p->y;
1046 /* read subsequent points */
1047 if (proto < 22)
1048 npts = 1000000; /* loop until we find 9999 9999 for previous fig files */
1049 cnpts = 1; /* keep track of actual number of points read */
1050 for (--npts; npts > 0; npts--) {
1051 count_lines_correctly(fp);
1052 if (fscanf(fp, "%d%d", &x, &y) != 2) {
1053 file_msg(Err_incomp, "line", save_line);
1054 free_linestorage(l);
1055 numcom=0;
1056 return NULL;
1057 }
1058 if (proto < 22 && x == 9999)
1059 break;
1060 /* ignore identical consecutive points */
1061 if (ox == x && oy == y)
1062 continue;
1063 ox = x;
1064 oy = y;
1065 if ((q = create_point()) == NULL) {
1066 free_linestorage(l);
1067 numcom=0;
1068 return NULL;
1069 }
1070 q->x = x;
1071 q->y = y;
1072 q->next = NULL;
1073 p->next = q;
1074 p = q;
1075 cnpts++;
1076 }
1077 /* also, if it has fewer than 5 points and is a box, picture, or arcbox,
1078 or if it has fewer than 3 points and it is a polygon remove it */
1079 if ((cnpts < 5 && (l->type == T_BOX || l->type == T_ARCBOX || l->type == T_PICTURE)) ||
1080 (cnpts < 3 && l->type == T_POLYGON)) {
1081 if (l->type == T_POLYGON) {
1082 file_msg("Deleting polygon containing fewer than 3 points at line %d",
1083 save_line);
1084 } else {
1085 file_msg("Deleting zero-size %s at line %d",
1086 l->type==T_BOX? "box" : l->type==T_ARCBOX? "arcbox" : "picture",
1087 save_line);
1088 }
1089 free_linestorage(l);
1090 numcom=0;
1091 return NULL;
1092 }
1093 /* if the line has only one point, delete any arrowheads it might have now */
1094 if (l->points->next == NULL) {
1095 if (l->for_arrow) {
1096 free((char *) l->for_arrow);
1097 l->for_arrow = (F_arrow *) NULL;
1098 }
1099 if (l->back_arrow) {
1100 free((char *) l->back_arrow);
1101 l->back_arrow = (F_arrow *) NULL;
1102 }
1103 }
1104 l->comments = attach_comments(); /* attach any comments */
1105 /* skip to the next line */
1106 skip_line(fp);
1107 return l;
1108 }
1109
1110 static F_spline *
read_splineobject(FILE * fp)1111 read_splineobject(FILE *fp)
1112 {
1113 F_spline *s;
1114 F_point *p, *q;
1115 F_sfactor *cp, *cq;
1116 int c, n, x, y, fa, ba, npts, numpts;
1117 int type, style;
1118 float thickness, wd, ht;
1119 double s_param;
1120 float lx, ly, rx, ry;
1121
1122 if ((s = create_spline()) == NULL){
1123 numcom=0;
1124 return NULL;
1125 }
1126
1127 save_line = line_no;
1128 s->points = NULL;
1129 s->sfactors = NULL;
1130 s->for_arrow = s->back_arrow = NULL;
1131 s->next = NULL;
1132
1133 /* 3.0(experimental 2.2) or later has number of points parm for all spline
1134 objects and fill color separate from border color */
1135 if (proto >= 30) {
1136 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%d%f%d%d%d%d",
1137 &s->type, &s->style, &s->thickness, &s->pen_color, &s->fill_color,
1138 &s->depth, &s->pen_style, &s->fill_style, &s->style_val,
1139 &s->cap_style, &fa, &ba, &npts);
1140 } else {
1141 n = sscanf(buf, "%*d%d%d%d%d%d%d%d%f%d%d",
1142 &s->type, &s->style, &s->thickness, &s->pen_color,
1143 &s->depth, &s->pen_style, &s->fill_style, &s->style_val, &fa, &ba);
1144 s->fill_color = s->pen_color;
1145 s->cap_style = CAP_BUTT; /* butt line cap */
1146 }
1147 if (((proto < 22) && (n != 10)) || ((proto >= 30) && n != 13)) {
1148 file_msg(Err_incomp, "spline", save_line);
1149 free((char *) s);
1150 numcom=0;
1151 return NULL;
1152 }
1153 s->fill_style = FILL_CONVERT(s->fill_style);
1154 fix_depth(&s->depth);
1155 check_color(&s->pen_color);
1156 check_color(&s->fill_color);
1157 fix_fillstyle(s); /* make sure that black/white have legal fill styles */
1158 /* forward arrow */
1159 if (fa) {
1160 if (read_line(fp) == -1){
1161 numcom=0;
1162 return NULL;
1163 }
1164 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
1165 file_msg(Err_incomp, "spline", save_line);
1166 numcom=0;
1167 return NULL;
1168 }
1169 /* make sure arrowhead is legal and convert units */
1170 convert_arrow(&type, &wd, &ht);
1171 s->for_arrow = new_arrow(type, style, thickness, wd, ht);
1172 }
1173 /* backward arrow */
1174 if (ba) {
1175 if (read_line(fp) == -1){
1176 numcom=0;
1177 return NULL;
1178 }
1179 if (sscanf(buf, "%d%d%f%f%f", &type, &style, &thickness, &wd, &ht) != 5) {
1180 file_msg(Err_incomp, "spline", save_line);
1181 numcom=0;
1182 return NULL;
1183 }
1184 /* make sure arrowhead is legal and convert units */
1185 convert_arrow(&type, &wd, &ht);
1186 s->back_arrow = new_arrow(type, style, thickness, wd, ht);
1187 }
1188
1189 /* read first point */
1190 line_no++;
1191 if ((n = fscanf(fp, "%d%d", &x, &y)) != 2) {
1192 file_msg(Err_incomp, "spline", save_line);
1193 free_splinestorage(s);
1194 numcom=0;
1195 return NULL;
1196 };
1197 if ((p = create_point()) == NULL) {
1198 free_splinestorage(s);
1199 numcom=0;
1200 return NULL;
1201 }
1202 s->points = p;
1203 p->x = x;
1204 p->y = y;
1205 c = 1;
1206 /* read subsequent points */
1207 if (proto < 22)
1208 npts = 1000000; /* loop until we find 9999 9999 for previous fig files */
1209 numpts = 1;
1210 for (--npts; npts > 0; npts--) {
1211 count_lines_correctly(fp);
1212 if (fscanf(fp, "%d%d", &x, &y) != 2) {
1213 file_msg(Err_incomp, "spline", save_line);
1214 p->next = NULL;
1215 free_splinestorage(s);
1216 numcom=0;
1217 return NULL;
1218 };
1219 if (proto < 22 && x == 9999)
1220 break;
1221 if ((q = create_point()) == NULL) {
1222 free_splinestorage(s);
1223 numcom=0;
1224 return NULL;
1225 }
1226 q->x = x;
1227 q->y = y;
1228 p->next = q;
1229 p = q;
1230 c++;
1231 numpts++;
1232 }
1233 p->next = NULL;
1234
1235 if (proto <= 31) { /* to read files from version 3.1 and older */
1236 if int_spline(s) {
1237 /* 2 control points per point given by user in
1238 version 3.1 and older : don't read them */
1239 while (c--) {
1240 count_lines_correctly(fp);
1241 if (fscanf(fp, "%f%f%f%f", &lx, &ly, &rx, &ry) != 4) {
1242 file_msg(Err_incomp, "spline", save_line);
1243 free_splinestorage(s);
1244 numcom=0;
1245 return NULL;
1246 }
1247 }
1248 }
1249 if (closed_spline(s)) {
1250 F_point *ptr = s->points;
1251 s->points = s->points->next;
1252 free (ptr);
1253 }
1254 if (! make_sfactors(s)) {
1255 free_splinestorage(s);
1256 numcom=0;
1257 return NULL;
1258 }
1259 return s;
1260 }
1261
1262 /* Read sfactors - the s parameter for splines */
1263
1264 count_lines_correctly(fp);
1265 if ((n = fscanf(fp, "%lf", &s_param)) != 1) {
1266 file_msg(Err_incomp, "spline", save_line);
1267 free_splinestorage(s);
1268 numcom=0;
1269 return NULL;
1270 };
1271 if ((cp = create_sfactor()) == NULL) {
1272 free_splinestorage(s);
1273 numcom=0;
1274 return NULL;
1275 }
1276 s->sfactors = cp;
1277 cp->s = s_param;
1278 while (--c) {
1279 count_lines_correctly(fp);
1280 if (fscanf(fp, "%lf", &s_param) != 1) {
1281 file_msg(Err_incomp, "spline", save_line);
1282 cp->next = NULL;
1283 free_splinestorage(s);
1284 numcom=0;
1285 return NULL;
1286 };
1287 if ((cq = create_sfactor()) == NULL) {
1288 cp->next = NULL;
1289 free_splinestorage(s);
1290 numcom=0;
1291 return NULL;
1292 }
1293 cq->s=s_param;
1294 cp->next = cq;
1295 cp = cq;
1296 }
1297 if (closed_spline(s) && numpts < 3) {
1298 file_msg("Closed splines must have 3 or more points, removing spline at line %d", save_line);
1299 free_splinestorage(s);
1300 numcom=0;
1301 return NULL;
1302 } else if (numpts < 2) {
1303 file_msg("Open splines must have 2 or more points, removing spline at line %d", save_line);
1304 free_splinestorage(s);
1305 numcom=0;
1306 return NULL;
1307 }
1308 cp->next = NULL;
1309 s->comments = attach_comments(); /* attach any comments */
1310
1311 /* skip to the end of the line */
1312 skip_line(fp);
1313 return s;
1314 }
1315
1316 static F_text *
read_textobject(FILE * fp)1317 read_textobject(FILE *fp)
1318 {
1319 F_text *t;
1320 int l,n,len;
1321 int ignore = 0;
1322 char s[BUF_SIZE], s_temp[BUF_SIZE], junk[2];
1323 float tx_size;
1324 float length, height;
1325 Boolean more;
1326 PR_SIZE tx_dim;
1327
1328 if ((t = create_text()) == NULL){
1329 numcom=0;
1330 return NULL;
1331 }
1332
1333 save_line = line_no;
1334 t->next = NULL;
1335 /*
1336 * The text object is terminated by a CONTROL-A, so we read everything up
1337 * to the CONTROL-A and then read that character. If we do not find the
1338 * CONTROL-A on this line then this must be a multi-line text object and
1339 * we will have to read more.
1340 *
1341 * We read text size, height and length as floats because TransFig uses
1342 * floats for these, but they are rounded to ints internally to xfig.
1343 */
1344 /* read the leading blanks for the string, but delete the first one later */
1345
1346 /*
1347 * NOTE: The height, length and descent will be recalculated from the
1348 * actual font structure in read_scale_text().
1349 */
1350
1351 if (proto >= 30) { /* order of parms is more like other objects now;
1352 string is now terminated with the literal '\001',
1353 and 8-bit characters are represented as \xxx */
1354 n = sscanf(buf, "%*d%d%d%d%d%d%f%f%d%f%f%d%d%[^\n]",
1355 &t->type, &t->color, &t->depth, &t->pen_style,
1356 &t->font, &tx_size, &t->angle, &t->flags,
1357 &height, &length, &t->base_x, &t->base_y, s);
1358 } else {
1359 n = sscanf(buf, "%*d%d%d%f%d%d%d%f%d%f%f%d%d%[^\1]%[\1]",
1360 &t->type, &t->font, &tx_size, &t->pen_style,
1361 &t->color, &t->depth, &t->angle, &t->flags,
1362 &height, &length, &t->base_x, &t->base_y, s, junk);
1363 }
1364 /* remove newline */
1365 buf[strlen(buf)-1] = '\0';
1366 if (buf[strlen(buf)-1] == '\r')
1367 buf[strlen(buf)-1] = '\0';
1368 /* remove any trailing carriage returns (^M, possibly from a PC) */
1369 if (s[strlen(s)-1] == '\r')
1370 s[strlen(s)-1] = '\0';
1371 /* use these for now, but recalculate later in read_scale_text if not update_figs */
1372 t->ascent = round(height);
1373 t->descent = 0;
1374 t->length = round(length);
1375
1376 if (n < 11) {
1377 file_msg(Err_incomp, "text", save_line);
1378 free((char *) t);
1379 numcom=0;
1380 return NULL;
1381 }
1382
1383 /* now round size to int */
1384
1385 /* change DEFAULT (-1) or 0 size to default size */
1386 if ((int) tx_size == DEFAULT || (int) tx_size == 0)
1387 t->size = DEF_FONTSIZE;
1388 else
1389 t->size = round(tx_size);
1390
1391 /* set some limits */
1392 if (t->size < MIN_FONT_SIZE)
1393 t->size = MIN_FONT_SIZE;
1394 else if (t->size > MAX_FONT_SIZE)
1395 t->size = MAX_FONT_SIZE;
1396
1397 /* make sure angle is 0 to 2PI */
1398 fix_angle(&t->angle);
1399
1400 /* convert all pre-2.1 NON-TFX text flags (used to be font_style) to PostScript
1401 and all pre-2.1 TFX flags to PostScript + Special */
1402 if (proto <= 20) {
1403 t->flags = PSFONT_TEXT;
1404 if (TFX)
1405 t->flags |= SPECIAL_TEXT;
1406 }
1407
1408 /* check for valid font number */
1409 if (t->font >= MAXFONT(t)) {
1410 file_msg("Invalid text font (%d) at line %d, setting to DEFAULT.",
1411 t->font, save_line);
1412 t->font = DEFAULT;
1413 }
1414
1415 /* get the UNZOOMED font struct */
1416 if (!update_figs)
1417 t->fontstruct = lookfont(x_fontnum(psfont_text(t), t->font), t->size);
1418
1419 fix_depth(&t->depth);
1420 check_color(&t->color);
1421 more = False;
1422 if (proto < 22 && n == 13)
1423 more = True; /* in older xfig there is more if ^A wasn't found yet */
1424 else if (proto >= 30) { /* in 3.0(2.2) there is more if \001 wasn't found */
1425 len = strlen(s);
1426 if ((strcmp(&s[len-4],"\\001") == 0) && /* if we find '\000' */
1427 !(backslash_count(s, len-5) % 2)) { /* and not '\\000' */
1428 more = False; /* then there are no more lines */
1429 s[len-4]='\0'; /* and get rid of the '\001' */
1430 } else {
1431 more = True;
1432 s[len++]='\n'; /* put back the end of line char */
1433 s[len] = '\0'; /* and terminate it */
1434 }
1435 }
1436 if (more) {
1437 /* Read in the subsequent lines of the text object if there is more than one. */
1438 do {
1439 line_no++; /* As is done in read_line */
1440 if (fgets(buf, BUF_SIZE, fp) == NULL)
1441 break;
1442 /* remove newline */
1443 buf[strlen(buf)-1] = '\0';
1444 if (buf[strlen(buf)-1] == '\r')
1445 buf[strlen(buf)-1] = '\0';
1446 if (proto < 22) {
1447 n = sscanf(buf, "%[^\1]%[\1]", s_temp, junk);
1448 } else {
1449 strcpy(s_temp,buf);
1450 len = strlen(s_temp);
1451 if ((strncmp(&s_temp[len-4],"\\001",4) == 0) &&
1452 !(backslash_count(s, len-5) % 2)) {
1453 n=0; /* found the '\001', set n to stop */
1454 s_temp[len-4]='\0'; /* and get rid of the '\001' */
1455 } else {
1456 n=1; /* keep going (more lines) */
1457 }
1458 }
1459 /* Safety check */
1460 if (strlen(s) + 1 + strlen(s_temp) + 1 > BUF_SIZE) {
1461 /* Too many characters. Ignore the rest. */
1462 if (!ignore)
1463 file_msg("Truncating TEXT object to %d chars in line %d.",
1464 BUF_SIZE, save_line);
1465 ignore = 1;
1466 }
1467 if (!ignore)
1468 strcat(s, s_temp);
1469 } while (n == 1);
1470 }
1471 if (proto >= 30) {
1472 /* now convert any \xxx to ascii characters */
1473 if (strchr(s,'\\')) {
1474 int num;
1475 len = strlen(s);
1476 for (l=0,n=0; l < len; l++) {
1477 if (s[l]=='\\') {
1478 /* a backslash, see if a digit follows */
1479 if (l < len && isdigit(s[l+1])) {
1480 /* yes, allow exactly 3 digits following the \ for the octal value */
1481 if (sscanf(&s[l+1],"%3o",&num)!=1) {
1482 file_msg("Error in parsing text string on line.", save_line);
1483 free((char *) t);
1484 numcom=0;
1485 return NULL;
1486 }
1487 buf[n++]= (unsigned char) num; /* put char in */
1488 l += 3; /* skip over digits */
1489 } else {
1490 buf[n++] = s[++l]; /* some other escaped character */
1491 }
1492 } else {
1493 buf[n++] = s[l]; /* ordinary character */
1494 }
1495 }
1496 buf[n]='\0'; /* terminate */
1497 strcpy(s,buf); /* copy back to s */
1498 }
1499 }
1500
1501 if (t->type > T_RIGHT_JUSTIFIED) {
1502 file_msg("Invalid text justification at line %d, setting to LEFT.", save_line);
1503 t->type = T_LEFT_JUSTIFIED;
1504 }
1505
1506 if (strlen(s) <= 1) {
1507 s[0]=' ';s[1]=0;
1508 }
1509 /* skip first blank from input file by starting at s[1] */
1510 if ((t->cstring = new_string(strlen(&s[1]))) == NULL) {
1511 free((char *) t);
1512 numcom=0;
1513 return NULL;
1514 }
1515 /* copy string to text object */
1516 (void) strcpy(t->cstring, &s[1]);
1517
1518 if (!update_figs) {
1519 /* now calculate the actual length and height of the string in fig units */
1520 tx_dim = textsize(t->fontstruct, strlen(t->cstring), t->cstring);
1521 t->length = round(tx_dim.length);
1522 t->ascent = round(tx_dim.ascent);
1523 t->descent = round(tx_dim.descent);
1524 /* now get the zoomed font struct */
1525 t->zoom = zoomscale;
1526 if (display_zoomscale != 1.0)
1527 t->fontstruct = lookfont(x_fontnum(psfont_text(t), t->font),
1528 round(t->size*display_zoomscale));
1529 }
1530
1531 t->comments = attach_comments(); /* attach any comments */
1532 return t;
1533 }
1534
1535 /* akm 28/2/95 - count consecutive backslashes backwards */
1536 static int
backslash_count(char * cp,int start)1537 backslash_count(char *cp, int start)
1538 {
1539 int i, count = 0;
1540
1541 for(i=start; i>=0; i--) {
1542 if (cp[i] == '\\')
1543 count++;
1544 else
1545 break;
1546 }
1547 return count;
1548 }
1549
1550 /* attach comments together */
1551
1552 static char *
attach_comments(void)1553 attach_comments(void)
1554 {
1555 int i,len;
1556 char *comp;
1557
1558 if (appres.DEBUG && (numcom > 0))
1559 fprintf(stderr,"Comments:\n");
1560 /* add up length of all comment lines */
1561 len = 0;
1562 for (i=0; i<numcom; i++)
1563 len += strlen(comments[i])+1;
1564 if ((comp = new_string(len)) == NULL) {
1565 numcom = 0;
1566 return NULL;
1567 }
1568 /* now make them into one string */
1569 comp[0] = '\0';
1570 for (i=0; i<numcom; i++) {
1571 if (appres.DEBUG)
1572 fprintf(stderr,"%2d: %s\n",i,comments[i]);
1573 strcat(comp, comments[i]);
1574 /* don't put trailing newline for last comment line */
1575 if (i<numcom-1)
1576 strcat(comp,"\n");
1577 }
1578 /* reset comment number */
1579 numcom = 0;
1580 return comp;
1581 }
1582
1583 static int
read_line(FILE * fp)1584 read_line(FILE *fp)
1585 {
1586 while (1) {
1587 if (NULL == fgets(buf, BUF_SIZE, fp)) {
1588 return -1;
1589 }
1590 line_no++;
1591 if (*buf == '#') { /* save any comments */
1592 if (save_comment() < 0)
1593 return -1;
1594 } else if (*buf != '\n') /* Skip empty lines */
1595 return 1;
1596 }
1597 }
1598
1599 /* save a comment line to be stored with the *subsequent* object */
1600
1601 static int
save_comment(void)1602 save_comment(void)
1603 {
1604 size_t i;
1605
1606 /* skip too many comment lines */
1607 if (numcom == MAXCOMMENTS)
1608 return 2;
1609
1610 i = strlen(buf);
1611 /* see if we've allocated space for this comment */
1612 if (comments[numcom])
1613 free(comments[numcom]);
1614 if ((comments[numcom] = (char*)new_string(i+1)) == NULL)
1615 return -1;
1616 /* remove any newline */
1617 if (buf[i-1] == '\n')
1618 buf[i-1] = '\0';
1619 i = 1;
1620 if (buf[1] == ' ')
1621 /* remove one leading blank from the comment, if there is one */
1622 i = 2;
1623 strcpy(comments[numcom++], &buf[i]);
1624 return 1;
1625 }
1626
1627 /* skip to the end of the current line */
1628
1629 static void
skip_line(FILE * fp)1630 skip_line(FILE *fp)
1631 {
1632 while (fgetc(fp) != '\n') {
1633 if (feof(fp))
1634 return;
1635 }
1636 }
1637
1638 /* make sure angle is 0 to 2PI */
1639
1640 void
fix_angle(float * angle)1641 fix_angle(float *angle)
1642 {
1643 while (*angle < 0.0)
1644 *angle += M_2PI;
1645 while (*angle >= M_2PI)
1646 *angle -= M_2PI;
1647 }
1648
1649 static void
fix_depth(int * depth)1650 fix_depth(int *depth)
1651 {
1652 if (*depth>MAX_DEPTH) {
1653 *depth=MAX_DEPTH;
1654 file_msg("Depth > Maximum allowed (%d), setting to %d in line %d.",
1655 MAX_DEPTH, save_line, MAX_DEPTH);
1656 }
1657 else if (*depth<0 || proto<21) {
1658 *depth=0;
1659 if (proto>=21)
1660 file_msg("Depth < 0, setting to 0 in line %d.", save_line);
1661 }
1662 }
1663
1664
1665 static void
shift_figure(F_compound * obj)1666 shift_figure(F_compound *obj)
1667 {
1668 F_ellipse *e;
1669 F_arc *a;
1670 F_line *l;
1671 F_spline *s;
1672 F_compound *c;
1673 F_text *t;
1674 int lowx,lowy,dx,dy;
1675 int rnd;
1676 char shift_msg[] = "The figure has objects which have negative coordinates,\ndo you wish to shift it back on the page?";
1677
1678 /* if user is allowing negative coords, return */
1679 if (appres.allownegcoords)
1680 return;
1681
1682 lowx = obj->nwcorner.x;
1683 lowy = obj->nwcorner.y;
1684
1685 /* check if any part of the figure has negative coords */
1686 if (lowx >= 0 && lowy >= 0)
1687 return; /* no, ok */
1688
1689 /* ask the user */
1690 if (!preview_in_progress && (popup_query(QUERY_YESNO, shift_msg)==RESULT_NO))
1691 return;
1692
1693 /* shift the whole figure to keep it "on the page" */
1694 dx = dy = 0;
1695 rnd = point_spacing();
1696 if (lowx < 0) {
1697 dx = -lowx+rnd; /* and round up to small grid */
1698 }
1699 if (lowy < 0) {
1700 dy = -lowy+rnd;
1701 }
1702 if (!preview_in_progress)
1703 file_msg("Shifting entire figure %d units right and %d units down to keep on page.",
1704 dx,dy);
1705 for (e = obj->ellipses; e != NULL; e = e->next)
1706 translate_ellipse(e, dx, dy);
1707 for (a = obj->arcs; a != NULL; a = a->next)
1708 translate_arc(a, dx, dy);
1709 for (l = obj->lines; l != NULL; l = l->next)
1710 translate_line(l, dx, dy);
1711 for (s = obj->splines; s != NULL; s = s->next)
1712 translate_spline(s, dx, dy);
1713 for (c = obj->compounds; c != NULL; c = c->next)
1714 translate_compound(c, dx, dy);
1715 for (t = obj->texts; t != NULL; t = t->next)
1716 translate_text(t, dx, dy);
1717 }
1718
1719 static void
scale_figure(F_compound * obj,float mul,int offset)1720 scale_figure(F_compound *obj, float mul, int offset)
1721 {
1722 /* scale the whole figure for new pixels per inch */
1723 if (mul != 1.0)
1724 put_msg("Scaling figure by a factor of %.1f for new %d pixel per inch resolution.",
1725 mul,PIX_PER_INCH);
1726 read_scale_ellipses(obj->ellipses, mul, offset);
1727 read_scale_arcs(obj->arcs, mul, offset);
1728 read_scale_lines(obj->lines, mul, offset);
1729 read_scale_splines(obj->splines, mul, offset);
1730 read_scale_compounds(obj->compounds, mul, offset);
1731 read_scale_texts(obj->texts, mul, offset);
1732
1733 }
1734
1735 /* check if user color <color> is defined */
1736
1737 void
check_color(int * color)1738 check_color(int *color)
1739 {
1740 if (*color < NUM_STD_COLS)
1741 return;
1742 if (!n_colorFree[*color-NUM_STD_COLS])
1743 return;
1744 file_msg("Cannot locate user color %d, using default color for line %d.",
1745 *color,line_no);
1746 *color = DEFAULT;
1747 return;
1748 }
1749
1750 /* swap new colors (n_...) with current for file load or undo load */
1751
swap_colors(void)1752 void swap_colors(void)
1753 {
1754 int i,num;
1755 Boolean saveFree[MAX_USR_COLS];
1756
1757 if (appres.DEBUG)
1758 fprintf(stderr,"Swapping colors. Before: colors_are_swapped = %d\n",colors_are_swapped);
1759
1760 colors_are_swapped = True;
1761
1762 /* first save the current colors because del_color_cell destroys them */
1763 for (i=0; i<num_usr_cols; i++)
1764 save_colors[i] = user_colors[i];
1765 /* and save Free entries */
1766 for (i=0; i<num_usr_cols; i++)
1767 saveFree[i] = colorFree[i];
1768 /* now free any previously defined user colors */
1769 for (i=0; i<num_usr_cols; i++) {
1770 del_color_cell(i); /* remove widget and colormap entry */
1771 }
1772 /* now swap old colors with new */
1773 for (i=0; i<n_num_usr_cols; i++)
1774 user_colors[i] = n_user_colors[i];
1775 for (i=0; i<num_usr_cols; i++)
1776 n_user_colors[i] = save_colors[i];
1777 /* and swap Free entries */
1778 for (i=0; i<n_num_usr_cols; i++)
1779 colorFree[i] = n_colorFree[i];
1780 for (i=0; i<num_usr_cols; i++)
1781 n_colorFree[i] = saveFree[i];
1782
1783 num = num_usr_cols;
1784 num_usr_cols = n_num_usr_cols;
1785 n_num_usr_cols = num;
1786
1787 /* now try to allocate the new colors */
1788 if (num_usr_cols > 0) {
1789 num = num_usr_cols;
1790 num_usr_cols = 0;
1791 /* fill the colormap and the color memories */
1792 for (i=0; i<num; i++) {
1793 if (colorFree[i]) {
1794 colorUsed[i] = False;
1795 } else {
1796 /* and add a widget and colormap entry */
1797 if (add_color_cell(USE_EXISTING_COLOR, i, user_colors[i].red/256,
1798 user_colors[i].green/256,
1799 user_colors[i].blue/256) == -1) {
1800 file_msg("Can't allocate more than %d user colors, not enough colormap entries",
1801 num_usr_cols);
1802 return;
1803 }
1804 colorUsed[i] = True;
1805 }
1806 }
1807 }
1808 }
1809
1810 /* Merge any user colors from the new file just merged into the current figure.
1811 Look through the new color definitions n_... and see if any color numbers
1812 conflict with the current color defs. If so, renumber those to new, free
1813 color numbers.
1814 This is called when doing a "merge read" or a "paste" function. */
1815
1816 static int renum[MAX_USR_COLS];
1817
1818 static void
merge_colors(F_compound * objects)1819 merge_colors(F_compound *objects)
1820 {
1821 Boolean found_exist;
1822 int i,j,newval;
1823 int x_colorFree[MAX_USR_COLS];
1824
1825 if (n_num_usr_cols == 0)
1826 return;
1827
1828 newval = -1;
1829
1830 /* look for the first free color number */
1831 for (i=0; i<MAX_USR_COLS; i++)
1832 if (colorFree[i])
1833 break;
1834
1835 if (i<MAX_USR_COLS)
1836 newval = i;
1837
1838 /* we need a tri-state value for n_colorFree[] so make local copy */
1839 for (i=0; i<MAX_USR_COLS; i++)
1840 x_colorFree[i] = (int) n_colorFree[i];
1841
1842 for (i=0; i<n_num_usr_cols; i++) {
1843 renum[i] = -1;
1844 if (x_colorFree[i] == 0) {
1845 x_colorFree[i] = 1; /* we're not using this number anymore */
1846 /* see if it is identical to an existing color */
1847 found_exist = False;
1848 for (j=0; j<num_usr_cols; j++)
1849 /* compare only the upper 8-bits because the server may change the lower */
1850 if (colorUsed[j] &&
1851 (user_colors[j].red>>8 == n_user_colors[i].red>>8) &&
1852 (user_colors[j].green>>8 == n_user_colors[i].green>>8) &&
1853 (user_colors[j].blue>>8 == n_user_colors[i].blue>>8)) {
1854 renum[i] = j; /* yes, use it */
1855 found_exist=True;
1856 break; /* skip to next */
1857 }
1858 if (!found_exist) {
1859 if (newval == -1) {
1860 renum[i] = 0; /* out of user colors, use 0 */
1861 } else {
1862 renum[i] = newval; /* assign it a new color number */
1863 /* find the next free color number */
1864 while (newval < MAX_USR_COLS) {
1865 newval++;
1866 if (colorFree[newval])
1867 break;
1868 }
1869 if (newval >= MAX_USR_COLS)
1870 newval = -1;
1871 }
1872 x_colorFree[renum[i]] = -1; /* we are using this number now */
1873 n_user_colors[renum[i]] = n_user_colors[i]; /* copy rgb values */
1874 }
1875 }
1876 }
1877
1878 /* renumber them now */
1879 n_num_usr_cols = max2(newval,num_usr_cols); /* new upper limit on color number */
1880 renumber_comp(objects);
1881
1882 /* now create colorcells for the new colors */
1883 for (i=0; i<n_num_usr_cols; i++) {
1884 if (x_colorFree[i] != 1) {
1885 user_colors[i] = n_user_colors[i];
1886 /* and add a widget and colormap entry */
1887 if (add_color_cell(USE_EXISTING_COLOR, i,
1888 user_colors[i].red/256,
1889 user_colors[i].green/256,
1890 user_colors[i].blue/256) == -1) {
1891 file_msg("Can't allocate more than %d user colors, not enough colormap entries",
1892 n_num_usr_cols);
1893 return;
1894 }
1895 colorFree[i] = False;
1896 colorUsed[i] = True;
1897 }
1898 }
1899 num_usr_cols = n_num_usr_cols;
1900 }
1901
1902 static void
renumber_comp(F_compound * compound)1903 renumber_comp(F_compound *compound)
1904 {
1905 F_arc *a;
1906 F_text *t;
1907 F_compound *c;
1908 F_ellipse *e;
1909 F_line *l;
1910 F_spline *s;
1911
1912 /* first renumber colors in the compounds */
1913 for (c = compound->compounds; c != NULL; c = c->next)
1914 renumber_comp(c);
1915
1916 /* now the primitives */
1917 for (a = compound->arcs; a != NULL; a = a->next) {
1918 renumber(&a->fill_color);
1919 renumber(&a->pen_color);
1920 }
1921 for (t = compound->texts; t != NULL; t = t->next) {
1922 renumber(&t->color);
1923 }
1924 for (e = compound->ellipses; e != NULL; e = e->next) {
1925 renumber(&e->fill_color);
1926 renumber(&e->pen_color);
1927 }
1928 for (l = compound->lines; l != NULL; l = l->next) {
1929 renumber(&l->fill_color);
1930 renumber(&l->pen_color);
1931 }
1932 for (s = compound->splines; s != NULL; s = s->next) {
1933 renumber(&s->fill_color);
1934 renumber(&s->pen_color);
1935 }
1936 }
1937
1938 static void
renumber(int * color)1939 renumber(int *color)
1940 {
1941 if (*color < NUM_STD_COLS)
1942 return;
1943 if (renum[*color-NUM_STD_COLS] != -1)
1944 *color = renum[*color-NUM_STD_COLS]+NUM_STD_COLS;
1945 }
1946
1947 /* this function is to count line numbers correctly while reading
1948 * input files.
1949 * It skips all tabs and spaces and increments the global
1950 * variable line_no if a newline was found.
1951 * If any other character is read, it is put back to the input
1952 * stream and the function returns.
1953 * It should be called from within the point reading loops
1954 * in the read_{line,spline}object functions, where the point
1955 * coordinates may be given in an arbitrary number of lines.
1956 * Added by Andreas_Bagge@maush2.han.de (A.Bagge), 14.12.94
1957 */
1958
1959 static void
count_lines_correctly(FILE * fp)1960 count_lines_correctly(FILE *fp)
1961 {
1962 int cc;
1963 do{
1964 cc=getc(fp);
1965 if (cc=='\n') {
1966 line_no++;
1967 cc=getc(fp);
1968 }
1969 } while (cc==' '||cc=='\t');
1970 ungetc(cc,fp);
1971 }
1972
1973 /* make sure arrow style value is legal and convert arrow width and height to
1974 * same units as thickness in V4.0 and later we will save the values in these units */
1975
1976 static void
convert_arrow(int * type,float * wd,float * ht)1977 convert_arrow(int *type, float *wd, float *ht)
1978 {
1979 if (*type >= NUM_ARROW_TYPES/2)
1980 *type = 0;
1981 if (proto < 40) {
1982 *wd /= ZOOM_FACTOR;
1983 *ht /= ZOOM_FACTOR;
1984 }
1985 }
1986