source: branches/stable/WINDOW/AW_xfig.cxx

Last change on this file was 16766, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_xfig.cxx                                       //
4//   Purpose   : functions for processing xfig graphics            //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_xfig.hxx"
12#include <aw_device.hxx>
13#include "aw_msg.hxx"
14
15#include <arbdb.h>
16#include <climits>
17
18inline int scaleAndRound(int unscaled, double scaleFactor) {
19    double scaled = double(unscaled)*scaleFactor;
20    return AW_INT(scaled);
21}
22
23inline void setMinMax(int value, int& min, int& max) {
24    if (value<min) min = value;
25    if (value>max) max = value;
26}
27
28class Xfig_Eater {
29    char *buffer;
30    const char *delim;
31
32    char *p; // last return of strtok
33    bool failed;
34
35    void nextToken() {
36        if (!failed) {
37            p = strtok(buffer, delim);
38            buffer = NULp;
39            if (!p) failed = true;
40        }
41    }
42
43public:
44    Xfig_Eater(char *buffer_, const char *delim_) : buffer(buffer_), delim(delim_) {
45        p = NULp;
46        failed = false;
47    }
48
49    bool eat_int(int& what) {
50        nextToken();
51        if (failed) return false;
52        what = atoi(p);
53        return true;
54    }
55
56    bool ignore(unsigned count=1) {
57        while (count-->0) {
58            nextToken();
59            if (failed) return false;
60        }
61        return true;
62    }
63
64    char *get_rest() {
65        if (failed || !p) return NULp;
66        return p+strlen(p)+1;
67    }
68};
69
70void AW_xfig::calc_scaling(int font_width, int font_height) {
71    double font_x_scale = abs(font_width) / (double) XFIG_DEFAULT_FONT_WIDTH;
72    double font_y_scale = abs(font_height) / (double) XFIG_DEFAULT_FONT_HEIGHT;
73
74    font_scale = font_x_scale>font_y_scale ? font_x_scale : font_y_scale;
75    dpi_scale  = font_scale;
76}
77
78AW_xfig::AW_xfig(int font_width, int font_height) {
79    // creates the same as loading an empty xfig
80    init(font_width, font_height);
81}
82
83AW_xfig::AW_xfig(const char *filename, int font_width, int font_height) {
84    /* Arguments:   xfig file, fontsize (>0: abort on error - normal usage!
85     *                                   <0: returns NULp on error)
86     *
87     * Returns:     graphical data or NULp or exit!
88     *
89     * Description: load xfig graphical data for construction of windows,
90     */
91    aw_assert(filename && filename[0]);
92    init(font_width, font_height);
93
94    // ----------------
95
96    GB_ERROR  error  = NULp;
97    char     *ret;
98    char     *buffer = ARB_calloc<char>(MAX_XFIG_LENGTH);
99    FILE     *file   = NULp;
100
101    if (filename[0]=='/') { // absolute file ?
102        strcpy(buffer, filename);
103        file = fopen(buffer, "r");
104    }
105    else {
106        const char *fileInLib = GB_path_in_ARBLIB("pictures", filename);
107
108        strcpy(buffer, fileInLib);
109        file = fopen(fileInLib, "r");
110
111        // Note: before 12/2008 file was also searched in $ARBHOME and current dir
112    }
113
114    if (!file) {
115        error = GBS_global_string("Can't locate '%s'", filename);
116    }
117    else {
118        char *expanded_filename = strdup(buffer);
119        int   lineNumber        = 0;
120
121        ret = fgets(buffer, MAX_XFIG_LENGTH, file); ++lineNumber;
122        if (!ret || strncmp("#FIG", ret, 4)) {
123            error = "Expected XFIG format";
124        }
125        else {
126            enum {
127                XFIG_UNKNOWN,
128                XFIG_OLD_FORMAT, // XFIG 2.1 saves old format
129                XFIG_NEW_FORMAT, // XFIG 3.2 saves new format
130                XFIG_UNSUPPORTED
131
132            } version = XFIG_UNKNOWN;
133
134            char *xfig_version = strchr(ret, ' ');
135           
136            if (!xfig_version) {
137                error = "Missing version info";
138            }
139            else {
140                int   mainVersion  = 0;
141                int   subVersion   = 0;
142                *xfig_version++ = 0;
143                mainVersion = atoi(xfig_version);
144
145                char *vers_point = strchr(xfig_version, '.');
146                if (vers_point) {
147                    *vers_point++ = 0;
148                    subVersion = atoi(vers_point);
149                }
150                else {
151                    subVersion = 0; // none
152                }
153
154                if (mainVersion>3 || (mainVersion==3 && subVersion>2)) {
155                    version = XFIG_UNSUPPORTED; // unsupported (maybe only untested)
156                    error = "Xfig-format above 3.2 not supported";
157                }
158                else {
159                    if (mainVersion==3 && subVersion==2) {
160                        version = XFIG_NEW_FORMAT;
161                    }
162                    else {
163                        version = XFIG_OLD_FORMAT;
164                    }
165                }
166            }
167            if (!error) {
168                ret             = fgets(buffer, MAX_XFIG_LENGTH, file); ++lineNumber;
169                if (!ret) error = "Unexpected end of file";
170            }
171
172            if (!error) {
173                at_pos_hash = GBS_create_hash(100, GB_MIND_CASE);
174
175                maxx = maxy = 0;
176                minx = miny = INT_MAX;
177
178                if (version==XFIG_NEW_FORMAT) { // XFIG 3.2 format
179                    // new xfig format has the following changes:
180                    //
181                    //  - header (see just below)
182                    //  - lines do not end with 9999 9999
183                    //  - ??? maybe more changes
184
185
186                    // over-read xfig-header:
187                    // Landscape
188                    // Center
189                    // Metric
190                    // A4
191                    // 100.00
192                    // Single
193                    // -2
194
195                    int count;
196                    for (count = 0;
197                         ret && count<=6;
198                         ret=fgets(buffer, MAX_XFIG_LENGTH, file), count++, ++lineNumber)
199                    {
200                        const char *awaited = NULp;
201                        switch (count) {
202                            case 0: awaited = "Landscape"; break;
203                            case 1: awaited = "Center"; break;
204                            case 2: awaited = "Metric"; break;
205                            case 3: awaited = "A4"; break;
206                            case 4: awaited = ""; break; // accept any magnification (accepted only 100 before)
207                            case 5: awaited = "Single"; break;
208                            case 6: awaited = "-2"; break;
209                            default: aw_assert(0);
210                        }
211
212                        if (strncmp(ret, awaited, strlen(awaited))!=0) {
213                            error = GBS_global_string("'%s' expected", awaited);
214                        }
215                    }
216                }
217            }
218
219            if (!error) {
220                // read resolution
221                int dpi = 80;
222                int default_dpi = 80; // used in old version (before 3.2)
223                if (ret) {
224                    char *p = strtok(ret, "\t");
225                    if (p) dpi = atoi(p);
226
227                    ret = fgets(buffer, MAX_XFIG_LENGTH, file); ++lineNumber;
228
229                    if (dpi!=default_dpi) dpi_scale = font_scale * (double(default_dpi)/double(dpi));
230                }
231
232                while (ret) {
233                    bool  got_nextline = false;
234                    char *p;
235                    int   width        = 0;
236                    int   color        = 0;
237                    int   x, y;
238
239                    if (ret[0]=='2') {  // lines
240                        int oldx = 0, oldy = 0;
241
242                        {
243                            Xfig_Eater args(ret, " \t");
244
245                            bool ok =
246                                args.ignore(3) &&       // ignore '2', type, depth
247                                args.eat_int(width) &&  // width
248                                args.eat_int(color);    // color
249
250                            if (!ok) break;
251                        }
252
253                        while (1) {
254                            ret = fgets(buffer, MAX_XFIG_LENGTH, file); ++lineNumber;
255                            if (!ret) break;
256                            if (ret[0]!='\t') {
257                                got_nextline = true;
258                                break;
259                            }
260
261                            Xfig_Eater args(ret, " \t");
262                            bool ok = true;
263                            oldx = oldy = INT_MAX;
264
265                            while (ok) {
266                                ok = args.eat_int(x) && args.eat_int(y);
267                                if (!ok) break;
268
269                                // 9999/9999 is the end of line-points marker in old version
270                                if (version==XFIG_OLD_FORMAT && x==9999 && y==9999) break;
271
272                                x = scaleAndRound(x, dpi_scale);
273                                y = scaleAndRound(y, dpi_scale);
274
275                                setMinMax(x, minx, maxx);
276                                setMinMax(y, miny, maxy);
277
278                                aw_assert(x>=0 && y>=0);
279
280                                if (oldx == INT_MAX && oldy == INT_MAX) {
281                                    oldx = x;
282                                    oldy = y;
283                                    continue;
284                                }
285
286                                struct AW_xfig_line *xline = new AW_xfig_line;
287                                if (width >= MAX_LINE_WIDTH) width = MAX_LINE_WIDTH - 1;
288                                xline->next = line[width];
289                                line[width] = xline;
290                                xline->x0 = oldx;
291                                xline->y0 = oldy;
292                                xline->x1 = x;
293                                xline->y1 = y;
294                                oldx = x;
295                                oldy = y;
296                                xline->color = color;
297                            }
298                        }
299
300                    }
301                    else if (ret[0]=='4') { // text
302                        int align;
303                        int fontnr   = -1;
304                        int fontsize = -1;
305
306                        // old format: 4 align font  fontsize   depth   color ???       angle justi flags width x y text
307                        // new format: 4 align color depth      ???     font  fontsize  angle justi flags width x y text
308                        //                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309
310                        Xfig_Eater args(ret, " \t");
311
312                        bool ok =
313                            args.ignore(1) &&       // the '4'
314                            args.eat_int(align);    // align
315
316                        if (ok) {
317                            if (version==XFIG_OLD_FORMAT) {
318                                ok =
319                                    args.eat_int(fontnr) &&         // font
320                                    args.eat_int(fontsize) &&       // fontsize
321                                    args.ignore(1) &&               // depth
322                                    args.eat_int(color) &&          // color
323                                    args.ignore(1);                 // ???
324                            }
325                            else {
326                                aw_assert(version==XFIG_NEW_FORMAT);
327
328                                ok =
329                                    args.eat_int(color) &&          // color
330                                    args.ignore(1) &&               // depth
331                                    args.ignore(1) &&               // ???
332                                    args.eat_int(fontnr) &&         // font
333                                    args.eat_int(fontsize);         // fontsize
334                            }
335
336                            if (ok) {
337                                ok =
338                                    args.ignore(3) &&           // angle, justi, flags
339                                    args.eat_int(width) &&      // width
340                                    args.eat_int(x) &&          // x
341                                    args.eat_int(y);            // y
342
343                            }
344                        }
345
346                        if (ok && (p=args.get_rest())) {
347                            x = scaleAndRound(x, dpi_scale);
348                            y = scaleAndRound(y, dpi_scale);
349
350                            while (*p==' ' || *p=='\t') ++p;
351
352                            char *endf = strchr(p, 1); // search for ASCII-1 (new EOL-marker)
353                            char *endf2 = GBS_find_string(p, "\\001", 0); // search for "\001" (Pseudo-ASCII-1)
354                            if (endf || endf2) {
355                                if (endf) *endf = 0;
356                                if (endf2) *endf2 = 0;
357                            }
358
359                            if (*p=='$') {      // text starts with a '$'
360                                // place a button
361                                if (strcmp(p, "$$") == 0) {
362                                    this->centerx = x;
363                                    this->centery = y;
364                                }
365                                else {
366                                    struct AW_xfig_pos *xpos = new AW_xfig_pos;
367
368                                    xpos->center = align;
369                                    xpos->x      = x;
370                                    xpos->y      = y;
371
372                                    GBS_write_hash(at_pos_hash, p+1, (long)xpos);
373                                }
374                            }
375                            else {
376                                struct AW_xfig_text *xtext = new AW_xfig_text;
377                                xtext->x = x;
378                                xtext->y = y;
379
380                                if (x>maxx) maxx = x;
381                                if (y>maxy) maxy = y;
382                                if (x<minx) minx = x;
383                                if (y<miny) miny = y;
384
385                                xtext->text = strdup(p);
386                                xtext->fontsize = fontsize;
387                                xtext->color = color;
388                                xtext->center = align;
389                                xtext->font = fontnr;
390                                xtext->next = text;
391                                text = xtext;
392                            }
393                        }
394
395                    }
396
397                    if (!got_nextline) {
398                        ret = fgets(buffer, MAX_XFIG_LENGTH, file); ++lineNumber;
399                    }
400                }
401
402                this->size_x = maxx - minx;
403                this->size_y = maxy - miny;
404            }
405        }
406
407        if (error) { // append file-info to error
408            aw_assert(expanded_filename);
409            error = GBS_global_string("While reading %s:%i:\nError: %s", expanded_filename, lineNumber, error);
410        }
411
412        free(expanded_filename);
413        fclose(file);
414    }
415
416    free(buffer);
417
418    if (error) {
419        error = GBS_global_string("Failed to read XFIG resource (defect installation?)\n"
420                                  "Reason: %s", error);
421
422        if (font_width>0 && font_height>0) { // react with fatal exit
423            GBK_terminate(error);
424        }
425
426        // special case (used by aw_read_xfigfont())
427        aw_message(error);
428    }
429}
430
431static long aw_xfig_hash_free_loop(const char *, long val, void *) {
432    free((void*)val);
433    return 0;
434}
435
436AW_xfig::~AW_xfig() {
437    int i;
438
439    if (at_pos_hash) {
440        GBS_hash_do_loop(at_pos_hash, aw_xfig_hash_free_loop, NULp);
441        GBS_free_hash(at_pos_hash);
442    }
443    struct AW_xfig_text *xtext;
444    while (text) {
445        xtext = text;
446        text = text->next;
447        delete xtext->text;
448        delete xtext;
449    }
450
451    struct AW_xfig_line *xline;
452    for (i=0; i<MAX_LINE_WIDTH; i++) {
453        while (line[i]) {
454            xline = line[i];
455            line[i] = xline->next;
456            delete xline;
457        }
458    }
459}
460
461void AW_xfig::print(AW_device *device) {
462    const AW_screen_area&  window_size = device->get_area_size();
463    device->clear(-1);
464    struct AW_xfig_text   *xtext;
465    for (xtext = text; xtext; xtext=xtext->next) {
466        char *str = xtext->text;
467
468        if (str[0]) {
469            int   x   = xtext->x;
470            int   y   = xtext->y;
471
472            if (str[1]) {
473                if (str[1] == ':') {
474                    if (str[0] == 'Y') {
475                        y   += (window_size.b - window_size.t)- size_y;
476                        str += 2;
477                    }
478                    else if (str[0] == 'X') {
479                        x   += (window_size.r - window_size.l)- size_x;
480                        str += 2;
481                    }
482                }
483                else if (str[2] == ':' && str[0] == 'X' && str[1] == 'Y') {
484                    x   += (window_size.r - window_size.l)- size_x;
485                    y   += (window_size.b - window_size.t)- size_y;
486                    str += 3;
487                }
488            }
489
490            device->text(xtext->gc, str, (AW_pos)x, (AW_pos)y, (AW_pos)xtext->center*.5, AW_ALL_DEVICES_UNSCALED);
491        }
492    }
493
494    struct AW_xfig_line *xline;
495    for (int i=0; i<MAX_LINE_WIDTH; i++) {
496        int line_width = std::max(1, scaleAndRound(i, font_scale));
497        device->set_line_attributes(0, line_width, AW_SOLID);
498        for (xline = line[i]; xline; xline=xline->next) {
499            device->line(0, (AW_pos)xline->x0, (AW_pos)xline->y0, (AW_pos)xline->x1, (AW_pos)xline->y1);
500        }
501    }
502}
503
504void AW_xfig::create_gcs(AW_device *device, int depth) {
505    GB_HASH *gchash;
506    int gc;
507    char fontstring[100];
508    gchash = GBS_create_hash(100, GB_MIND_CASE);
509
510    struct AW_xfig_text *xtext;
511    gc = 0;
512    device->new_gc(gc);   // create at least one gc ( 0 ) for the lines
513    device->set_foreground_color(gc, AW_WINDOW_FG);
514    if (depth<=1) device->set_function(gc, AW_XOR);
515    device->set_line_attributes(gc, 1, AW_SOLID);
516    gc = 1;         // create gc for texts
517    for (xtext = text; xtext; xtext=xtext->next) {
518        sprintf(fontstring, "%i-%i", xtext->font, scaleAndRound(xtext->fontsize, font_scale));
519        if (!(xtext->gc = (int)GBS_read_hash(gchash, fontstring))) {
520            device->new_gc(gc);
521            device->set_line_attributes(gc, 1, AW_SOLID);
522            device->set_font(gc, xtext->font, scaleAndRound(xtext->fontsize, font_scale), NULp);
523            device->set_foreground_color(gc, AW_WINDOW_FG);
524            if (depth<=1) device->set_function(gc, AW_XOR);
525            xtext->gc = gc;
526            GBS_write_hash(gchash, fontstring, gc);
527            gc++;
528        }
529    }
530    GBS_free_hash(gchash);
531}
532
533void AW_xfig::add_line(int x1, int y1, int x2, int y2, int width) { // add a line to xfig
534    struct AW_xfig_line *xline = new AW_xfig_line;
535
536    x1 = scaleAndRound(x1, dpi_scale);
537    x2 = scaleAndRound(x2, dpi_scale);
538    y1 = scaleAndRound(y1, dpi_scale);
539    y2 = scaleAndRound(y2, dpi_scale);
540
541    setMinMax(x1, minx, maxx);
542    setMinMax(y1, miny, maxy);
543    setMinMax(x2, minx, maxx);
544    setMinMax(y2, miny, maxy);
545
546    xline->x0 = x1;
547    xline->y0 = y1;
548    xline->x1 = x2;
549    xline->y1 = y2;
550
551    xline->color = 1;
552
553    if (width >= MAX_LINE_WIDTH) width = MAX_LINE_WIDTH - 1;
554
555    xline->next = line[width];
556    line[width] = xline;
557}
Note: See TracBrowser for help on using the repository browser.