1 /*
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This is a plug-in for GIMP.
5 *
6 * Generates images containing vector type drawings.
7 *
8 * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24 #include "config.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #include <glib/gstdio.h>
35
36 #include <libgimp/gimp.h>
37 #include <libgimp/gimpui.h>
38
39 #include "gfig.h"
40 #include "gfig-style.h"
41 #include "gfig-dialog.h"
42 #include "gfig-arc.h"
43 #include "gfig-bezier.h"
44 #include "gfig-circle.h"
45 #include "gfig-dobject.h"
46 #include "gfig-ellipse.h"
47 #include "gfig-grid.h"
48 #include "gfig-line.h"
49 #include "gfig-poly.h"
50 #include "gfig-preview.h"
51 #include "gfig-spiral.h"
52 #include "gfig-star.h"
53 #include "gfig-stock.h"
54
55 #include "libgimp/stdplugins-intl.h"
56
57
58 #define GFIG_HEADER "GFIG Version 0.2\n"
59
60 static void query (void);
61 static void run (const gchar *name,
62 gint nparams,
63 const GimpParam *param,
64 gint *nreturn_vals,
65 GimpParam **return_vals);
66
67
68 const GimpPlugInInfo PLUG_IN_INFO =
69 {
70 NULL, /* init_proc */
71 NULL, /* quit_proc */
72 query, /* query_proc */
73 run, /* run_proc */
74 };
75
76
77 gint line_no;
78
79 gint obj_show_single = -1; /* -1 all >= 0 object number */
80
81 /* Structures etc for the objects */
82 /* Points used to draw the object */
83
84 GfigObject *obj_creating; /* Object we are creating */
85 GfigObject *tmp_line; /* Needed when drawing lines */
86
87 gboolean need_to_scale;
88
89 static gint load_options (GFigObj *gfig,
90 FILE *fp);
91 /* globals */
92
93 GfigObjectClass dobj_class[10];
94 GFigContext *gfig_context;
95 GtkWidget *top_level_dlg;
96 GList *gfig_list;
97 gdouble org_scale_x_factor, org_scale_y_factor;
98
99
100 /* Stuff for the preview bit */
101 static gint sel_x, sel_y;
102 static gint sel_width, sel_height;
103 gint preview_width, preview_height;
104 gdouble scale_x_factor, scale_y_factor;
105 GdkPixbuf *back_pixbuf = NULL;
106
MAIN()107 MAIN ()
108
109 static void
110 query (void)
111 {
112 static const GimpParamDef args[] =
113 {
114 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
115 { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
116 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
117 { GIMP_PDB_INT32, "dummy", "dummy" }
118 };
119
120 gimp_install_procedure (PLUG_IN_PROC,
121 N_("Create geometric shapes"),
122 "Draw Vector Graphics and paint them onto your images. "
123 "Gfig allows you to draw many types of objects "
124 "including Lines, Circles, Ellipses, Curves, Polygons, "
125 "pointed stars, Bezier curves, and Spirals. "
126 "Objects can be painted using Brushes or other tools"
127 "or filled using colors or patterns. "
128 "Gfig objects can also be used to create selections. ",
129 "Andy Thomas",
130 "Andy Thomas",
131 "1997",
132 N_("_Gfig..."),
133 "RGB*, GRAY*",
134 GIMP_PLUGIN,
135 G_N_ELEMENTS (args), 0,
136 args, NULL);
137
138 gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render");
139 }
140
141 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)142 run (const gchar *name,
143 gint nparams,
144 const GimpParam *param,
145 gint *nreturn_vals,
146 GimpParam **return_vals)
147 {
148 static GimpParam values[1];
149 gint32 drawable_id;
150 GimpRunMode run_mode;
151 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
152 gint pwidth, pheight;
153
154 INIT_I18N ();
155
156 gfig_context = g_new0 (GFigContext, 1);
157 gfig_context->show_background = TRUE;
158 gfig_context->selected_obj = NULL;
159
160 drawable_id = param[2].data.d_drawable;
161
162 run_mode = param[0].data.d_int32;
163
164 gfig_context->image_id = param[1].data.d_image;
165 gfig_context->drawable_id = drawable_id;
166
167 *nreturn_vals = 1;
168 *return_vals = values;
169
170 values[0].type = GIMP_PDB_STATUS;
171 values[0].data.d_status = status;
172
173 gimp_image_undo_group_start (gfig_context->image_id);
174
175 gimp_context_push ();
176
177 /* TMP Hack - clear any selections */
178 if (! gimp_selection_is_empty (gfig_context->image_id))
179 gimp_selection_none (gfig_context->image_id);
180
181 if (! gimp_drawable_mask_intersect (drawable_id, &sel_x, &sel_y,
182 &sel_width, &sel_height))
183 {
184 gimp_context_pop ();
185
186 gimp_image_undo_group_end (gfig_context->image_id);
187 return;
188 }
189
190 /* Calculate preview size */
191
192 if (sel_width > sel_height)
193 {
194 pwidth = MIN (sel_width, PREVIEW_SIZE);
195 pheight = sel_height * pwidth / sel_width;
196 }
197 else
198 {
199 pheight = MIN (sel_height, PREVIEW_SIZE);
200 pwidth = sel_width * pheight / sel_height;
201 }
202
203
204 preview_width = MAX (pwidth, 2); /* Min size is 2 */
205 preview_height = MAX (pheight, 2);
206
207 org_scale_x_factor = scale_x_factor =
208 (gdouble) sel_width / (gdouble) preview_width;
209 org_scale_y_factor = scale_y_factor =
210 (gdouble) sel_height / (gdouble) preview_height;
211
212 /* initialize */
213 gfig_init_object_classes ();
214
215 switch (run_mode)
216 {
217 case GIMP_RUN_INTERACTIVE:
218 case GIMP_RUN_WITH_LAST_VALS:
219 /*gimp_get_data (PLUG_IN_PROC, &selvals);*/
220 if (! gfig_dialog ())
221 {
222 gimp_image_undo_group_end (gfig_context->image_id);
223
224 return;
225 }
226 break;
227
228 case GIMP_RUN_NONINTERACTIVE:
229 status = GIMP_PDB_CALLING_ERROR;
230 break;
231
232 default:
233 break;
234 }
235
236 gimp_context_pop ();
237
238 gimp_image_undo_group_end (gfig_context->image_id);
239
240 if (run_mode != GIMP_RUN_NONINTERACTIVE)
241 gimp_displays_flush ();
242 else
243 #if 0
244 if (run_mode == GIMP_RUN_INTERACTIVE)
245 gimp_set_data (PLUG_IN_PROC, &selvals, sizeof (SelectItVals));
246 else
247 #endif /* 0 */
248 {
249 status = GIMP_PDB_EXECUTION_ERROR;
250 }
251
252 values[0].data.d_status = status;
253 }
254
255 /*
256 Translate SPACE to "\\040", etc.
257 Taken from gflare plugin
258 */
259 void
gfig_name_encode(gchar * dest,gchar * src)260 gfig_name_encode (gchar *dest,
261 gchar *src)
262 {
263 gint cnt = MAX_LOAD_LINE - 1;
264
265 while (*src && cnt--)
266 {
267 if (g_ascii_iscntrl (*src) || g_ascii_isspace (*src) || *src == '\\')
268 {
269 sprintf (dest, "\\%03o", *src++);
270 dest += 4;
271 }
272 else
273 *dest++ = *src++;
274 }
275 *dest = '\0';
276 }
277
278 /*
279 Translate "\\040" to SPACE, etc.
280 */
281 void
gfig_name_decode(gchar * dest,const gchar * src)282 gfig_name_decode (gchar *dest,
283 const gchar *src)
284 {
285 gint cnt = MAX_LOAD_LINE - 1;
286 guint tmp;
287
288 while (*src && cnt--)
289 {
290 if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
291 {
292 sscanf (src+1, "%3o", &tmp);
293 *dest++ = tmp;
294 src += 4;
295 }
296 else
297 *dest++ = *src++;
298 }
299 *dest = '\0';
300 }
301
302
303 /*
304 * Load all gfig, which are founded in gfig-path-list, into gfig_list.
305 * gfig-path-list must be initialized first. (plug_in_parse_gfig_path ())
306 * based on code from Gflare.
307 */
308
309 gint
gfig_list_pos(GFigObj * gfig)310 gfig_list_pos (GFigObj *gfig)
311 {
312 GFigObj *g;
313 gint n;
314 GList *tmp;
315
316 n = 0;
317
318 for (tmp = gfig_list; tmp; tmp = g_list_next (tmp))
319 {
320 g = tmp->data;
321
322 if (strcmp (gfig->draw_name, g->draw_name) <= 0)
323 break;
324
325 n++;
326 }
327 return n;
328 }
329
330 /*
331 * Insert gfigs in alphabetical order
332 */
333
334 gint
gfig_list_insert(GFigObj * gfig)335 gfig_list_insert (GFigObj *gfig)
336 {
337 gint n;
338
339 n = gfig_list_pos (gfig);
340
341 gfig_list = g_list_insert (gfig_list, gfig, n);
342
343 return n;
344 }
345
346 void
gfig_free(GFigObj * gfig)347 gfig_free (GFigObj *gfig)
348 {
349 g_assert (gfig != NULL);
350
351 free_all_objs (gfig->obj_list);
352
353 g_free (gfig->name);
354 g_free (gfig->filename);
355 g_free (gfig->draw_name);
356
357 g_free (gfig);
358 }
359
360 GFigObj *
gfig_new(void)361 gfig_new (void)
362 {
363 return g_new0 (GFigObj, 1);
364 }
365
366 static void
gfig_load_objs(GFigObj * gfig,gint load_count,FILE * fp)367 gfig_load_objs (GFigObj *gfig,
368 gint load_count,
369 FILE *fp)
370 {
371 GfigObject *obj;
372 gchar load_buf[MAX_LOAD_LINE];
373 glong offset;
374 glong offset2;
375 Style style;
376
377 while (load_count-- > 0)
378 {
379 obj = NULL;
380 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
381
382 /* kludge */
383 offset = ftell (fp);
384 gfig_skip_style (&style, fp);
385
386 obj = d_load_object (load_buf, fp);
387
388 if (obj)
389 {
390 add_to_all_obj (gfig, obj);
391 offset2 = ftell (fp);
392 fseek (fp, offset, SEEK_SET);
393 gfig_load_style (&obj->style, fp);
394 fseek (fp, offset2, SEEK_SET);
395 }
396 else
397 {
398 g_message ("Failed to load object, load count = %d", load_count);
399 }
400 }
401 }
402
403 GFigObj *
gfig_load(const gchar * filename,const gchar * name)404 gfig_load (const gchar *filename,
405 const gchar *name)
406 {
407 GFigObj *gfig;
408 FILE *fp;
409 gchar load_buf[MAX_LOAD_LINE];
410 gchar str_buf[MAX_LOAD_LINE];
411 gint chk_count;
412 gint load_count = 0;
413 gdouble version;
414 gchar magic1[20];
415 gchar magic2[20];
416
417 g_assert (filename != NULL);
418
419 #ifdef DEBUG
420 printf ("Loading %s (%s)\n", filename, name);
421 #endif /* DEBUG */
422
423 fp = g_fopen (filename, "rb");
424 if (!fp)
425 {
426 g_message (_("Could not open '%s' for reading: %s"),
427 gimp_filename_to_utf8 (filename), g_strerror (errno));
428 return NULL;
429 }
430
431 gfig = gfig_new ();
432
433 gfig->name = g_strdup (name);
434 gfig->filename = g_strdup (filename);
435
436
437 /* HEADER
438 * draw_name
439 * version
440 * obj_list
441 */
442
443 get_line (load_buf, MAX_LOAD_LINE, fp, 1);
444
445 sscanf (load_buf, "%10s %10s %lf", magic1, magic2, &version);
446
447 if (strcmp (magic1, "GFIG") || strcmp (magic2, "Version"))
448 {
449 g_message ("File '%s' is not a gfig file",
450 gimp_filename_to_utf8 (gfig->filename));
451 gfig_free (gfig);
452 fclose (fp);
453 return NULL;
454 }
455
456 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
457 sscanf (load_buf, "Name: %100s", str_buf);
458 gfig_name_decode (load_buf, str_buf);
459 gfig->draw_name = g_strdup (load_buf);
460
461 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
462 if (strncmp (load_buf, "Version: ", 9) == 0)
463 gfig->version = g_ascii_strtod (load_buf + 9, NULL);
464
465 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
466 sscanf (load_buf, "ObjCount: %d", &load_count);
467
468 if (load_options (gfig, fp))
469 {
470 g_message ("File '%s' corrupt file - Line %d Option section incorrect",
471 gimp_filename_to_utf8 (filename), line_no);
472 gfig_free (gfig);
473 fclose (fp);
474 return NULL;
475 }
476
477 if (gfig_load_styles (gfig, fp))
478 {
479 g_message ("File '%s' corrupt file - Line %d Option section incorrect",
480 gimp_filename_to_utf8 (filename), line_no);
481 gfig_free (gfig);
482 fclose (fp);
483 return NULL;
484 }
485
486
487
488 gfig_load_objs (gfig, load_count, fp);
489
490 /* Check count ? */
491
492 chk_count = g_list_length (gfig->obj_list);
493
494 if (chk_count != load_count)
495 {
496 g_message ("File '%s' corrupt file - Line %d Object count to small",
497 gimp_filename_to_utf8 (filename), line_no);
498 gfig_free (gfig);
499 fclose (fp);
500 return NULL;
501 }
502
503 fclose (fp);
504
505 if (!gfig_context->current_obj)
506 gfig_context->current_obj = gfig;
507
508 gfig->obj_status = GFIG_OK;
509
510 return gfig;
511 }
512
513 void
save_options(GString * string)514 save_options (GString *string)
515 {
516 /* Save options */
517 g_string_append_printf (string, "<OPTIONS>\n");
518 g_string_append_printf (string, "GridSpacing: %d\n",
519 selvals.opts.gridspacing);
520 if (selvals.opts.gridtype == RECT_GRID)
521 {
522 g_string_append_printf (string, "GridType: RECT_GRID\n");
523 }
524 else if (selvals.opts.gridtype == POLAR_GRID)
525 {
526 g_string_append_printf (string, "GridType: POLAR_GRID\n");
527 }
528 else if (selvals.opts.gridtype == ISO_GRID)
529 {
530 g_string_append_printf (string, "GridType: ISO_GRID\n");
531 }
532 else
533 {
534 /* default to RECT_GRID */
535 g_string_append_printf (string, "GridType: RECT_GRID\n");
536 }
537
538 g_string_append_printf (string, "DrawGrid: %s\n",
539 (selvals.opts.drawgrid) ? "TRUE" : "FALSE");
540 g_string_append_printf (string, "Snap2Grid: %s\n",
541 (selvals.opts.snap2grid) ? "TRUE" : "FALSE");
542 g_string_append_printf (string, "LockOnGrid: %s\n",
543 (selvals.opts.lockongrid) ? "TRUE" : "FALSE");
544 g_string_append_printf (string, "ShowControl: %s\n",
545 (selvals.opts.showcontrol) ? "TRUE" : "FALSE");
546 g_string_append_printf (string, "</OPTIONS>\n");
547 }
548
549 static void
gfig_save_obj_start(GfigObject * obj,GString * string)550 gfig_save_obj_start (GfigObject *obj,
551 GString *string)
552 {
553 g_string_append_printf (string, "<%s ", obj->class->name);
554 gfig_style_save_as_attributes (&obj->style, string);
555 g_string_append_printf (string, ">\n");
556 }
557
558 static void
gfig_save_obj_end(GfigObject * obj,GString * string)559 gfig_save_obj_end (GfigObject *obj,
560 GString *string)
561 {
562 g_string_append_printf (string, "</%s>\n",obj->class->name);
563 }
564
565 static gboolean
load_bool(gchar * opt_buf,gint * toset)566 load_bool (gchar *opt_buf,
567 gint *toset)
568 {
569 if (!strcmp (opt_buf, "TRUE"))
570 *toset = 1;
571 else if (!strcmp (opt_buf, "FALSE"))
572 *toset = 0;
573 else
574 return TRUE;
575
576 return FALSE;
577 }
578
579 static gint
load_options(GFigObj * gfig,FILE * fp)580 load_options (GFigObj *gfig,
581 FILE *fp)
582 {
583 gchar load_buf[MAX_LOAD_LINE];
584 gchar str_buf[MAX_LOAD_LINE];
585 gchar opt_buf[MAX_LOAD_LINE];
586
587 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
588
589 #ifdef DEBUG
590 printf ("load '%s'\n", load_buf);
591 #endif /* DEBUG */
592
593 if (strcmp (load_buf, "<OPTIONS>"))
594 return (-1);
595
596 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
597
598 #ifdef DEBUG
599 printf ("opt line '%s'\n", load_buf);
600 #endif /* DEBUG */
601
602 while (strcmp (load_buf, "</OPTIONS>"))
603 {
604 /* Get option name */
605 #ifdef DEBUG
606 printf ("num = %d\n", sscanf (load_buf, "%255s %255s", str_buf, opt_buf));
607
608 printf ("option %s val %s\n", str_buf, opt_buf);
609 #else
610 sscanf (load_buf, "%255s %255s", str_buf, opt_buf);
611 #endif /* DEBUG */
612
613 if (!strcmp (str_buf, "GridSpacing:"))
614 {
615 /* Value is decimal */
616 int sp = 0;
617 sp = atoi (opt_buf);
618 if (sp <= 0)
619 return (-1);
620 gfig->opts.gridspacing = sp;
621 }
622 else if (!strcmp (str_buf, "DrawGrid:"))
623 {
624 /* Value is bool */
625 if (load_bool (opt_buf, &gfig->opts.drawgrid))
626 return (-1);
627 }
628 else if (!strcmp (str_buf, "Snap2Grid:"))
629 {
630 /* Value is bool */
631 if (load_bool (opt_buf, &gfig->opts.snap2grid))
632 return (-1);
633 }
634 else if (!strcmp (str_buf, "LockOnGrid:"))
635 {
636 /* Value is bool */
637 if (load_bool (opt_buf, &gfig->opts.lockongrid))
638 return (-1);
639 }
640 else if (!strcmp (str_buf, "ShowControl:"))
641 {
642 /* Value is bool */
643 if (load_bool (opt_buf, &gfig->opts.showcontrol))
644 return (-1);
645 }
646 else if (!strcmp (str_buf, "GridType:"))
647 {
648 /* Value is string */
649 if (!strcmp (opt_buf, "RECT_GRID"))
650 gfig->opts.gridtype = RECT_GRID;
651 else if (!strcmp (opt_buf, "POLAR_GRID"))
652 gfig->opts.gridtype = POLAR_GRID;
653 else if (!strcmp (opt_buf, "ISO_GRID"))
654 gfig->opts.gridtype = ISO_GRID;
655 else
656 return (-1);
657 }
658
659 get_line (load_buf, MAX_LOAD_LINE, fp, 0);
660
661 #ifdef DEBUG
662 printf ("opt line '%s'\n", load_buf);
663 #endif /* DEBUG */
664 }
665 return (0);
666 }
667
668 GString *
gfig_save_as_string(void)669 gfig_save_as_string (void)
670 {
671 GList *objs;
672 gint count;
673 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
674 gchar conv_buf[MAX_LOAD_LINE * 3 + 1];
675 GString *string;
676
677 string = g_string_new (GFIG_HEADER);
678
679 gfig_name_encode (conv_buf, gfig_context->current_obj->draw_name);
680 g_string_append_printf (string, "Name: %s\n", conv_buf);
681 g_string_append_printf (string, "Version: %s\n",
682 g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
683 gfig_context->current_obj->version));
684 objs = gfig_context->current_obj->obj_list;
685
686 count = g_list_length (objs);
687
688 g_string_append_printf (string, "ObjCount: %d\n", count);
689
690 save_options (string);
691
692 gfig_save_styles (string);
693
694 for (objs = gfig_context->current_obj->obj_list;
695 objs;
696 objs = g_list_next (objs))
697 {
698 GfigObject *object = objs->data;
699
700 gfig_save_obj_start (object, string);
701
702 gfig_save_style (&object->style, string);
703
704 if (object->points)
705 d_save_object (object, string);
706
707 gfig_save_obj_end (object, string);
708 }
709
710 return string;
711 }
712
713
714 gboolean
gfig_save_as_parasite(void)715 gfig_save_as_parasite (void)
716 {
717 GimpParasite *parasite;
718 GString *string;
719
720 string = gfig_save_as_string ();
721
722 parasite = gimp_parasite_new ("gfig",
723 GIMP_PARASITE_PERSISTENT |
724 GIMP_PARASITE_UNDOABLE,
725 string->len, string->str);
726
727 g_string_free (string, TRUE);
728
729 if (!gimp_item_attach_parasite (gfig_context->drawable_id, parasite))
730 {
731 g_message (_("Error trying to save figure as a parasite: "
732 "can't attach parasite to drawable."));
733 gimp_parasite_free (parasite);
734 return FALSE;
735 }
736
737 gimp_parasite_free (parasite);
738 return TRUE;
739 }
740
741 GFigObj *
gfig_load_from_parasite(void)742 gfig_load_from_parasite (void)
743 {
744 FILE *fp;
745 gchar *fname;
746 GimpParasite *parasite;
747 GFigObj *gfig;
748
749 parasite = gimp_item_get_parasite (gfig_context->drawable_id, "gfig");
750 if (! parasite)
751 return NULL;
752
753 fname = gimp_temp_name ("gfigtmp");
754
755 fp = g_fopen (fname, "wb");
756 if (!fp)
757 {
758 g_message (_("Error trying to open temporary file '%s' "
759 "for parasite loading: %s"),
760 gimp_filename_to_utf8 (fname), g_strerror (errno));
761 return NULL;
762 }
763
764 fwrite (gimp_parasite_data (parasite),
765 sizeof (guchar),
766 gimp_parasite_data_size (parasite),
767 fp);
768 fclose (fp);
769
770 gimp_parasite_free (parasite);
771
772 gfig = gfig_load (fname, "(none)");
773
774 g_unlink (fname);
775
776 g_free (fname);
777
778 return gfig;
779 }
780
781 void
gfig_save_callbk(void)782 gfig_save_callbk (void)
783 {
784 FILE *fp;
785 gchar *savename;
786 GString *string;
787
788 savename = gfig_context->current_obj->filename;
789
790 fp = g_fopen (savename, "w+b");
791
792 if (!fp)
793 {
794 g_message (_("Could not open '%s' for writing: %s"),
795 gimp_filename_to_utf8 (savename), g_strerror (errno));
796 return;
797 }
798
799 string = gfig_save_as_string ();
800
801 fwrite (string->str, string->len, 1, fp);
802
803 if (ferror (fp))
804 g_message ("Failed to write file.");
805 else
806 gfig_context->current_obj->obj_status &= ~(GFIG_MODIFIED | GFIG_READONLY);
807
808 fclose (fp);
809 }
810