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 | |
---|
18 | inline int scaleAndRound(int unscaled, double scaleFactor) { |
---|
19 | double scaled = double(unscaled)*scaleFactor; |
---|
20 | return AW_INT(scaled); |
---|
21 | } |
---|
22 | |
---|
23 | inline void setMinMax(int value, int& min, int& max) { |
---|
24 | if (value<min) min = value; |
---|
25 | if (value>max) max = value; |
---|
26 | } |
---|
27 | |
---|
28 | class 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 | |
---|
43 | public: |
---|
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 | |
---|
70 | void 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 | |
---|
78 | AW_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 | |
---|
83 | AW_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 | |
---|
431 | static long aw_xfig_hash_free_loop(const char *, long val, void *) { |
---|
432 | free((void*)val); |
---|
433 | return 0; |
---|
434 | } |
---|
435 | |
---|
436 | AW_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 | |
---|
461 | void 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 | |
---|
504 | void 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 | |
---|
533 | void 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 | } |
---|