1 /*
2  * TransFig: Facility for Translating Fig code
3  * Copyright (c) 1998 by Mike Markowski
4  * Copyright (c) 1991 by Micah Beck
5  * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
6  * Parts Copyright (c) 1989-2002 by Brian V. Smith
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
11  * documentation files (the "Software"), including without limitation the
12  * rights to use, copy, modify, merge, publish and/or distribute 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 this copyright
15  * notice remain intact.
16  *
17  */
18 
19 /*
20  *	genptk : Perl/Tk driver for fig2dev
21  *
22  *	Author: Slaven Rezic (rezic@onlineoffice.de) 8/2000
23  *      based on code by Mike Markowski (mm@udel.edu), U of Delaware, 4/98
24  */
25 
26 #include "fig2dev.h"
27 #include "object.h"
28 #include "readxbm.h"
29 
30 #define X(x) ((double)(x)/ppi)
31 #define Y(y) ((double)(y)/ppi)
32 
33 #define NONE (0xffffff + 1)
34 
35 static void
36 	drawBitmap(F_line *),
37 	drawShape(void (*)(), void *, int, int, int, int),
38 	niceLine(char *),
39 	ptkArc(void *, unsigned int, unsigned int, unsigned int, int),
40 	ptkEllipse(void *, unsigned int, unsigned int, unsigned int, int),
41 	ptkLine(void *, unsigned int, unsigned int, unsigned int, int),
42 	ptkPolygon(void *, unsigned int, unsigned int, unsigned int, int);
43 
44 static unsigned int
45 	rgbColorVal(int);
46 
47 static char *
48 	stippleFilename(int);
49 
50 static char *canvas = "$c";
51 static char *xbmPathVar = "$xbmPath";
52 static char *xbmPathName = BITMAPDIR;
53 
54 static char pngRequired  = 0;
55 static char jpegRequired = 0;
56 static char tiffRequired = 0;
57 
58 #define		TOP	8.5 /* inches */
59 static int	full_page = False;
60 
61 /*
62  *   g e n p T k O p t i o n ( )
63  */
64 
65 void
genptk_option(opt,optarg)66 genptk_option(opt, optarg)
67 char opt, *optarg;
68 {
69     switch (opt) {
70 	case 'l':			/* landscape mode */
71 		landscape = True;	/* override the figure file setting */
72 		orientspec = True;	/* user-specified */
73 		break;
74 
75 	case 'p':			/* portrait mode */
76 		landscape = False;	/* override the figure file setting */
77 		orientspec = True;	/* user-specified */
78 		break;
79 
80 	case 'P':			/* use full page instead of bounding box */
81 		full_page = True;
82 		break;
83 
84 	case 'z':			/* papersize */
85 		(void) strcpy (papersize, optarg);
86 		paperspec = True;	/* user-specified */
87 		break;
88 
89 	case 'f':			/* ignore magnification, font sizes and lang here */
90 	case 'm':
91 	case 's':
92 	case 'L':
93 		break;
94 
95 	default:
96 		put_msg(Err_badarg, opt, "tk");
97 		exit(1);
98     }
99 
100 }
101 
102 /*
103  *   g e n p T k S t a r t ( )
104  *
105  *   Any initialization garbage is taken care of here.
106  */
107 
108 void
genptk_start(F_compound * objects)109 genptk_start(F_compound *objects)
110 {
111 	char		stfp[1024];
112 	float		wid, ht, swap;
113 	struct paperdef	*pd;
114 
115 	ppi = ppi / mag;
116 
117 	/* print any whole-figure comments prefixed with "#" */
118 	if (objects->comments) {
119 	    fprintf(tfp,"#\n");
120 	    print_comments("# ",objects->comments, "");
121 	    fprintf(tfp,"#\n");
122 	}
123 
124 	if ( !full_page ) {
125 	    /* get width and height in fig units */
126 	    wid = urx - llx;
127 	    ht = ury - lly;
128 
129 	    /* add 1% border around */
130 	    llx -= round(wid/100.0);
131 	    lly -= round(ht/100.0);
132 	    urx += round(wid/100.0);
133 	    ury += round(ht/100.0);
134 
135 	    /* recalculate new width and height in inches */
136 	    wid = 1.0*(urx - llx)/ppi;
137 	    ht = 1.0*(ury - lly)/ppi;
138 
139 	} else {
140 	    /* full page, get the papersize as the width and height for the canvas */
141             for (pd = paperdef; pd->name != NULL; pd++)
142 		if (strcasecmp (papersize, pd->name) == 0) {
143 		    /* width/height are in dpi, convert to inches */
144 		    wid = pd->width/80.0;
145 		    ht = pd->height/80.0;
146 		    strcpy(papersize,pd->name);	/* use the "nice" form */
147 		    break;
148 		}
149 
150 	    if (wid < 0 || ht < 0) {
151 		(void) fprintf (stderr, "Unknown paper size `%s'\n", papersize);
152 		exit (1);
153 	    }
154 
155 	    sprintf(stfp, "# Page size specified: %s\n",papersize);
156 	    niceLine(stfp);
157 
158 	    /* swap for landscape */
159 	    if ( landscape) {
160 		sprintf(stfp, "# Landscape orientation\n");
161 		niceLine(stfp);
162 		swap = wid;
163 		wid = ht;
164 		ht = swap;
165 	    } else {
166 		sprintf(stfp, "# Portrait orientation\n");
167 		niceLine(stfp);
168 	    }
169 	}
170 
171 	sprintf(stfp, "sub {\n");
172 	niceLine(stfp);
173 	sprintf(stfp, "my $top = shift;\n");
174 	niceLine(stfp);
175 	sprintf(stfp, "my %%img;\n");
176 	niceLine(stfp);
177 	sprintf(stfp, "my $c = $top->Canvas(qw/-width %.2fi -height %.2fi -bg ivory/);\n", wid, ht);
178 	niceLine(stfp);
179 	sprintf(stfp, "#$c->configure(qw/-xscrollincrement 1p -yscrollincrement 1p/);\n");
180 	niceLine(stfp);
181 	if ( !full_page ) {
182 	    sprintf(stfp, "$c->configure(-scrollregion => ['%.2fi','%.2fi','%.2fi','%.2fi']);\n",
183 			    MIN(X(urx),X(llx)), MIN(Y(ury),Y(lly)),MAX(X(urx),X(llx)), MAX(Y(ury),Y(lly)));
184 	    niceLine(stfp);
185 	    sprintf(stfp, "# Shift canvas by lower of bounding box\n");
186 	    niceLine(stfp);
187 	    sprintf(stfp, "#$c->xview(qw/scroll %d u/);\n",round(llx/16.45*mag));
188 	    niceLine(stfp);
189 	    sprintf(stfp, "#$c->yview(qw/scroll %d u/);\n",round(lly/16.45*mag));
190 	    niceLine(stfp);
191 	}
192 
193 	sprintf(stfp, "$c->pack(-expand => 1, -fill => 'both');\n");
194 	niceLine(stfp);
195 	sprintf(stfp, "# If your fill-pattern bitmaps will be elsewhere,\n");
196 	niceLine(stfp);
197 	sprintf(stfp, "# change the next line appropriately, and all will\n");
198 	niceLine(stfp);
199 	sprintf(stfp, "# work well without further modification.\n\n");
200 	niceLine(stfp);
201 	sprintf(stfp, "my %s = '%s';\n\n", xbmPathVar, xbmPathName);
202 	niceLine(stfp);
203 	sprintf(stfp, "# The xfig objects begin here.\n");
204 	niceLine(stfp);
205 }
206 
207 /*
208  *   g e n p T k E n d ( )
209  *
210  *   Last call!...
211  */
212 
213 int
genptk_end()214 genptk_end()
215 {
216 	char	stfp[64];
217 
218 	sprintf(stfp, "%s->focus;\n", canvas);
219 	sprintf(stfp, "\n}\n");
220 	niceLine(stfp);
221 
222 	/* all ok */
223 	return 0;
224 }
225 
226 /*
227  *   g e n p T k A r c ( )
228  *
229  *   The Fig arc is one of the few objects that is only a subset of its
230  *   equivalent Tk canvas object, since a Tk arc can be oval.  But that
231  *   has no bearing on the code written here, so this is a pretty
232  *   useless comment...
233  */
234 
235 void
genptk_arc(F_arc * a)236 genptk_arc(F_arc *a)
237 {
238     /* print any comments prefixed with "#" */
239     print_comments("# ",a->comments, "");
240 
241     if (a->style > 0)
242 	fprintf(stderr, "genptk_arc: only solid lines are supported by Tk.\n");
243     if (a->type == T_OPEN_ARC && (a->for_arrow || a->back_arrow))
244 	fprintf(stderr, "genptk_arc: arc arrows not supported by Tk.\n");
245     drawShape(ptkArc, (void *) a, a->thickness,
246 	a->pen_color, a->fill_color, a->fill_style);
247 }
248 
249 /*
250  *   g e n p T k E l l i p s e ( )
251  */
252 
253 void
genptk_ellipse(F_ellipse * e)254 genptk_ellipse(F_ellipse *e)
255 {
256     /* print any comments prefixed with "#" */
257     print_comments("# ",e->comments, "");
258 
259     switch (e->type) {
260 	case T_CIRCLE_BY_DIA:
261 	case T_CIRCLE_BY_RAD:
262 	case T_ELLIPSE_BY_DIA:
263 	case T_ELLIPSE_BY_RAD:
264 		if (e->style > 0)
265 			fprintf(stderr, "genptk_ellipse: only solid lines "
266 				"supported.\n");
267 		drawShape(ptkEllipse, (void *) e, e->thickness,
268 			e->pen_color, e->fill_color, e->fill_style);
269 		break;
270 	default:
271 		/* Stole this line from Netscape 3.03... */
272 		fprintf(stderr, "genptk_ellipse: Whatchew talkin' 'bout, "
273 			"Willis?\n");
274 		return;
275 		break;
276     }
277 }
278 
279 /*
280  *   g e n p T k L i n e ( )
281  */
282 
283 void
genptk_line(F_line * l)284 genptk_line(F_line *l)
285 {
286     /* print any comments prefixed with "#" */
287     print_comments("# ",l->comments, "");
288 
289     switch (l->type) {
290 	case T_ARC_BOX:	/* Fall through to T_BOX... */
291 		fprintf(stderr, "genptk_line: arc box not supported.\n");
292 	case T_BOX:
293 	case T_POLYLINE:
294 		/* Take care of filled regions first. */
295 		drawShape(ptkPolygon, (void *) l->points, 0,
296 			l->pen_color, l->fill_color, l->fill_style);
297 		/* Now draw line itself. */
298 		drawShape(ptkLine, (void *) l, l->thickness, l->pen_color,
299 			NONE, UNFILLED);
300 		break;
301 	case T_PIC_BOX:
302 		drawBitmap(l);
303 		break;
304 	case T_POLYGON:
305 		if (l->style > 0) {
306 			fprintf(stderr, "genptk_line: only solid line "
307 				"styles supported.\n");
308 		}
309 		drawShape(ptkPolygon, (void *) l->points, l->thickness,
310 			l->pen_color, l->fill_color, l->fill_style);
311 		break;
312 	default:
313 		fprintf(stderr, "genptk_line: Whatchew talkin' 'bout, Willis?\n");
314 		break;
315     }
316 }
317 
318 /*
319  *   d r a w B i t m a p ( )
320  */
321 
322 #define	ReadOK(file,buffer,len)	(fread(buffer, len, 1, file) != 0)
323 
324 static void
drawBitmap(F_line * l)325 drawBitmap(F_line *l)
326 {
327 	char	stfp[1024];
328 	double	dx, dy;
329 	F_pic	*p;
330 	unsigned char	buf[16];
331 	FILE	*fd;
332 	int	filtype;	/* file (0) or pipe (1) */
333 	int	stat;
334 	FILE	*open_picfile();
335 	void	close_picfile();
336 	char	xname[PATH_MAX];
337 	char    isphoto;
338 
339 	p = l->pic;
340 
341 	dx = l->points->next->next->x - l->points->x;
342 	dy = l->points->next->next->y - l->points->y;
343 	if (!(dx >= 0. && dy >= 0.))
344 		fprintf(stderr, "drawBitmap: rotated bitmaps not supprted"
345 			" by Tk.\n");
346 
347 	/* see if supported image format first */
348 
349 	if ((fd=open_picfile(p->file, &filtype, True, xname)) == NULL) {
350 	    fprintf(stderr,"drawBitmap: can't open bitmap file %s\n",p->file);
351 	    return;
352 	}
353 
354 	/* read header */
355 
356 	stat = ReadOK(fd,buf,6);
357 	close_picfile(fd,filtype);
358 	if (!stat) {
359 		fprintf(stderr,"drawBitmap: Bitmap file %s too short\n",p->file);
360 		return;
361 	}
362 
363 	isphoto = 0;
364 	if ((strncmp((char *) buf,"GIF",3) == 0) ||
365 	    (*buf == 'P'  && (*(buf+1) == '5' || *(buf+1) == '6')) /* PPM/PGM */ ||
366 	    (strncmp((char *) buf,"BM",2) == 0) /* Windows BMP */ ||
367 	    (strncmp((char *) buf,"/* XPM */",9) == 0) /* X11 Pixmap */ ) {
368 		isphoto = 1;
369 	} else if (strncmp((char *) buf+1,"PNG",3) == 0) {
370 		isphoto = 1;
371 		if (!pngRequired) {
372 			sprintf(stfp, "require Tk::PNG;\n");
373 			niceLine(stfp);
374 			pngRequired++;
375 		}
376 	} else if (strncmp((char *) buf, "\377\330\377\340", 4) == 0) /* JPEG */ {
377 		isphoto = 1;
378 		if (!jpegRequired) {
379 			sprintf(stfp, "require Tk::JPEG;\n");
380 			niceLine(stfp);
381 			jpegRequired++;
382 		}
383 	} else if ((strncmp((char *) buf,"MM\x00\x2a",4) == 0) /* TIFF BE */ ||
384 		   (strncmp((char *) buf,"II\x2a\x00",4) == 0) /* TIFF LE */ ) {
385 		isphoto = 1;
386 		if (!tiffRequired) {
387 			sprintf(stfp, "require Tk::TIFF;\n");
388 			niceLine(stfp);
389 			tiffRequired++;
390 		}
391 	}
392 
393 	if (isphoto) {
394 		/* GIF/PNG/TIFF/JPEG/PPM allright, create the image command */
395 		/* first make a name without the .gif part */
396 		char pname[PATH_MAX], *dot;
397 		strcpy(pname,p->file);
398 		if ((dot=strchr(pname,'.')))
399 			*dot='\0';
400 		/* image create */
401 		sprintf(stfp, "$img{\"%s\"} = $top->Photo(-file => \"%s\");\n",pname, p->file);
402 		niceLine(stfp);
403 		/* now the canvas image */
404 		sprintf(stfp, "%s->createImage(qw/%fi %fi -anchor nw -image/, $img{\"%s\"});",
405 			canvas, X(l->points->x), Y(l->points->y), pname);
406 		niceLine(stfp);
407 	} else {
408 	    /* Try for an X Bitmap file format. */
409 	    rewind(fd);
410 	    if (ReadFromBitmapFile(fd, &dx, &dy, &p->bitmap)) {
411 		sprintf(stfp, "%s->createBitmap(qw/%fi %fi -anchor nw",
412 			canvas, X(l->points->x), Y(l->points->y));
413 		niceLine(stfp);
414 		sprintf(stfp, " -bitmap/, \"@%s\"", p->file);
415 		niceLine(stfp);
416 		if (l->pen_color != BLACK_COLOR && l->pen_color != DEFAULT) {
417 			sprintf(stfp, ", -foreground => '#%6.6x'",
418 				rgbColorVal(l->pen_color));
419 			niceLine(stfp);
420 		}
421 		niceLine(stfp);
422 		if (l->fill_color != UNFILLED) {
423 			sprintf(stfp, ", -background => '#%6.6x'",
424 				rgbColorVal(l->fill_color));
425 			niceLine(stfp);
426 		}
427 		sprintf(stfp, ");\n");
428 		niceLine(stfp);
429 	    } else
430 		fprintf(stderr, "Only X bitmap, TIFF, JPEG, PPM and GIF picture objects "
431 			"are supported in Tk canvases.\n");
432 	}
433 }
434 
435 /*
436  *   g e n p T k T e x t ( )
437  */
438 
439 /* define the latex fonts in terms of the postscript fonts */
440 #define latexFontDefault	0
441 #define latexFontRoman		1
442 #define latexFontBold		3
443 #define latexFontItalic		2
444 #define latexFontSansSerif	17
445 #define latexFontTypewriter	13
446 
447 void
genptk_text(F_text * t)448 genptk_text(F_text * t)
449 {
450 	char		stfp[2048];
451 	int		i, j;
452 
453 	/* I'm sure I'm just too dense to have seen a better way of doing this... */
454 	static struct {
455 		char	*prefix,
456 			*suffix;
457 	} fontNames[36] = {
458 		{"-adobe-times-medium-r-normal--",
459 			"-0-0-0-p-0-iso8859-1"},
460 		{"-adobe-times-medium-r-normal--",
461 			"-0-0-0-p-0-iso8859-1"},
462 		{"-adobe-times-medium-i-normal--",
463 			"-0-0-0-p-0-iso8859-1"},
464 		{"-adobe-times-bold-r-normal--",
465 			"-0-0-0-p-0-iso8859-1"},
466 		{"-adobe-times-bold-i-normal--",
467 			"-0-0-0-p-0-iso8859-1"},
468 		{"-urw-itc avant garde-medium-r-normal-sans-",
469 			"-0-0-0-p-0-iso8859-1"},
470 		{"-urw-itc avant garde-medium-o-normal-sans-",
471 			"-0-0-0-p-0-iso8859-1"},
472 		{"-urw-itc avant garde-demi-r-normal-sans-",
473 			"-0-0-0-p-0-iso8859-1"},
474 		{"-urw-itc avant garde-demi-o-normal-sans-",
475 			"-0-0-0-p-0-iso8859-1"},
476 		{"-urw-itc bookman-light-r-normal--",
477 			"-0-0-0-p-0-iso8859-1"},
478 		{"-urw-itc bookman-light-i-normal--",
479 			"-0-0-0-p-0-iso8859-1"},
480 		{"-urw-itc bookman-demi-r-normal--",
481 			"-0-0-0-p-0-iso8859-1"},
482 		{"-urw-itc bookman-demi-i-normal--",
483 			"-0-0-0-p-0-iso8859-1"},
484 		{"-adobe-courier-medium-r-normal--",
485 			"-0-0-0-m-0-iso8859-1"},
486 		{"-adobe-courier-medium-o-normal--",
487 			"-0-0-0-m-0-iso8859-1"},
488 		{"-adobe-courier-bold-r-normal--",
489 			"-0-0-0-m-0-iso8859-1"},
490 		{"-adobe-courier-bold-o-normal--",
491 			"-0-0-0-m-0-iso8859-1"},
492 		{"-adobe-helvetica-medium-r-normal--",
493 			"-0-0-0-p-0-iso8859-1"},
494 		{"-adobe-helvetica-medium-o-normal--",
495 			"-0-0-0-p-0-iso8859-1"},
496 		{"-adobe-helvetica-bold-r-normal--",
497 			"-0-0-0-p-0-iso8859-1"},
498 		{"-adobe-helvetica-bold-o-normal--",
499 			"-0-0-0-p-0-iso8859-1"},
500 		{"-adobe-helvetica-medium-r-narrow--",
501 			"-0-0-0-p-0-iso8859-1"},
502 		{"-adobe-helvetica-medium-o-narrow--",
503 			"-0-0-0-p-0-iso8859-1"},
504 		{"-adobe-helvetica-bold-r-narrow--",
505 			"-0-0-0-p-0-iso8859-1"},
506 		{"-adobe-helvetica-bold-o-narrow--",
507 			"-0-0-0-p-0-iso8859-1"},
508 		{"-adobe-new century schoolbook-medium-r-normal--",
509 			"-0-0-0-p-0-iso8859-1"},
510 		{"-adobe-new century schoolbook-medium-i-normal--",
511 			"-0-0-0-p-0-iso8859-1"},
512 		{"-adobe-new century schoolbook-bold-r-normal--",
513 			"-0-0-0-p-0-iso8859-1"},
514 		{"-adobe-new century schoolbook-bold-i-normal--",
515 			"-0-0-0-p-0-iso8859-1"},
516 		{"-adobe-palatino-medium-r-normal--",
517 			"-0-0-0-p-0-iso8859-1"},
518 		{"-adobe-palatino-medium-i-normal--",
519 			"-0-0-0-p-0-iso8859-1"},
520 		{"-adobe-palatino-bold-r-normal--",
521 			"-0-0-0-p-0-iso8859-1"},
522 		{"-adobe-palatino-medium-i-normal--",
523 			"-0-0-0-p-0-iso8859-1"},
524 		{"--symbol-medium-r-normal--",
525 			"-0-0-0-p-0-*-*"},
526 		{"-*-itc zapf chancery-medium-i-normal--",
527 			"-0-0-0-p-0-iso8859-1"},
528 		{"-*-itc zapf dingbats-medium-r-normal--",
529 			"-0-0-0-p-0-*-*"}
530 	};
531 
532 	/* print any comments prefixed with "#" */
533 	print_comments("# ",t->comments, "");
534 
535 	if (t->angle != 0.)
536 		fprintf(stderr, "genptk_text: rotated text not "
537 			"supported by Tk.\n");
538 
539 	sprintf(stfp, "%s->createText(qw/%fi %fi", canvas, X(t->base_x),
540 		Y(t->base_y));
541 	niceLine(stfp);
542 	strcpy(stfp, " -text/, '");
543 	j = strlen(stfp);
544 	for (i = 0; i < strlen(t->cstring); i++) {
545 		if (t->cstring[i] == '\'')
546 			stfp[j++] = '\\';
547 		stfp[j++] = t->cstring[i];
548 	}
549 	stfp[j++] = '\'';
550 	stfp[j++] = ',';
551 	stfp[j++] = '\0'; /* XXX ja? */
552 	niceLine(stfp);
553 	switch (t->type) {
554 	case T_LEFT_JUSTIFIED:
555 	case DEFAULT:
556 		sprintf(stfp, ", -anchor => 'sw'");
557 		break;
558 	case T_CENTER_JUSTIFIED:
559 		/* The Tk default. */
560 		sprintf(stfp, ", -anchor => 's'");
561 		break;
562 	case T_RIGHT_JUSTIFIED:
563 		sprintf(stfp, ", -anchor => 'se'");
564 		break;
565 	default:
566 		fprintf(stderr, "genptk_text: Unknown text justification\n");
567 		t->type = T_LEFT_JUSTIFIED;
568 		break;
569 	}
570 	niceLine(stfp);
571 
572 	if (psfont_text(t)) {
573 		sprintf(stfp, ", -font => \"%s%d%s\"", fontNames[t->font+1].prefix,
574 			(int) t->size, fontNames[t->font+1].suffix);
575 		niceLine(stfp);
576 	} else {	/* Rigid, special, and LaTeX fonts. */
577 		int fnum;
578 
579 		switch (t->font) {
580 		case 0:	/* Default font. */
581 			fnum = latexFontDefault;
582 			break;
583 		case 1:	/* Roman. */
584 			fnum = latexFontRoman;
585 			break;
586 		case 2:	/* Bold. */
587 			fnum = latexFontBold;
588 			break;
589 		case 3:	/* Italic. */
590 			fnum = latexFontItalic;
591 			break;
592 		case 4:	/* Sans Serif. */
593 			fnum = latexFontSansSerif;
594 			break;
595 		case 5:	/* Typewriter. */
596 			fnum = latexFontTypewriter;
597 			break;
598 		default:
599 			fnum = latexFontDefault;
600 			fprintf(stderr, "genptk_text: unknown LaTeX font.\n");
601 			break;
602 		}
603 		sprintf(stfp, ", -font => \"%s%d%s\"", fontNames[fnum].prefix,
604 			(int) t->size, fontNames[fnum].suffix);
605 		niceLine(stfp);
606 	}
607 	if (t->color != BLACK_COLOR && t->color != DEFAULT) {
608 		sprintf(stfp, ", -fill => '#%6.6x'", rgbColorVal(t->color));
609 		niceLine(stfp);
610 	}
611 	sprintf(stfp, ");\n");
612 	niceLine(stfp);
613 }
614 
615 /*
616  *   b e z i e r S p l i n e ( )
617  *
618  *   We could use the "-smooth" option of Tk's "line" canvas object to
619  *   get a Bezier spline.  But just to be sure everything is identical,
620  *   we'll do it ourselves.  All spline routines were blatantly swiped
621  *   from genibmgl.c with only non-mathematical mods.
622  */
623 
624 #define		THRESHOLD	.05	/* inch */
625 
626 static void
bezierSpline(double a0,double b0,double a1,double b1,double a2,double b2,double a3,double b3)627 bezierSpline(double a0, double b0, double a1, double b1, double a2, double b2,
628 	double a3, double b3)
629 {
630 	char	stfp[64];
631 	double	x0, y0, x3, y3;
632 	double	sx1, sy1, sx2, sy2, tx, ty, tx1, ty1, tx2, ty2, xmid, ymid;
633 
634 	x0 = a0; y0 = b0;
635 	x3 = a3; y3 = b3;
636 	if (fabs(x0 - x3) < THRESHOLD && fabs(y0 - y3) < THRESHOLD) {
637 		sprintf(stfp, ", '%.4fi', '%.4fi'", x3, y3);
638 		niceLine(stfp);
639 	} else {
640 		tx   = (a1  + a2 )/2.0;	ty   = (b1  + b2 )/2.0;
641 		sx1  = (x0  + a1 )/2.0;	sy1  = (y0  + b1 )/2.0;
642 		sx2  = (sx1 + tx )/2.0;	sy2  = (sy1 + ty )/2.0;
643 		tx2  = (a2  + x3 )/2.0;	ty2  = (b2  + y3 )/2.0;
644 		tx1  = (tx2 + tx )/2.0;	ty1  = (ty2 + ty )/2.0;
645 		xmid = (sx2 + tx1)/2.0;	ymid = (sy2 + ty1)/2.0;
646 
647 		bezierSpline(x0, y0, sx1, sy1, sx2, sy2, xmid, ymid);
648 		bezierSpline(xmid, ymid, tx1, ty1, tx2, ty2, x3, y3);
649 	}
650 }
651 
652 /*
653  *   g e n p T k I t p S p l i n e ( )
654  */
655 
656 static void
genptk_itpSpline(F_spline * s)657 genptk_itpSpline(F_spline *s)
658 {
659 	char		dir[8], stfp[1024];
660 	F_arrow		*a;
661 	F_point		*p1, *p2;
662 	F_control	*cp1, *cp2;
663 	double		x1, x2, y1, y2;
664 
665 	p1 = s->points;
666 	cp1 = s->controls;
667 	x2 = p1->x/ppi; y2 = p1->y/ppi;
668 
669 	sprintf(stfp, "%s->createLine(qw/%.4fi %.4fi/", canvas, x2, y2);
670 	niceLine(stfp);
671 	for (p2 = p1->next, cp2 = cp1->next; p2 != NULL;
672 		p1 = p2, cp1 = cp2, p2 = p2->next, cp2 = cp2->next) {
673 
674 		x1	 = x2;
675 		y1	 = y2;
676 		x2	 = p2->x/ppi;
677 		y2	 = p2->y/ppi;
678 		bezierSpline(x1, y1, (double)cp1->rx/ppi, cp1->ry/ppi,
679 			(double)cp2->lx/ppi, cp2->ly/ppi, x2, y2);
680 	}
681 
682 	a = NULL;
683 	if (s->for_arrow && !s->back_arrow) {
684 		a = s->for_arrow;
685 		strcpy(dir, "last");
686 	} else if (!s->for_arrow && s->back_arrow) {
687 		a = s->back_arrow;
688 		strcpy(dir, "first");
689 	} else if (s->for_arrow && s->back_arrow) {
690 		a = s->back_arrow;
691 		strcpy(dir, "both");
692 	}
693 	if (a)
694 		switch (a->type) {
695 		case 0:	/* Stick type. */
696 			sprintf(stfp, ", -arrow => '%s', -arrowshape => [0, '%fi', '%fi']",
697 				dir, X(a->ht), X(a->wid)/2.);
698 			niceLine(stfp);
699 			fprintf(stderr, "Warning: stick arrows do not "
700 				"work well in Tk.\n");
701 			break;
702 		case 1:	/* Closed triangle. */
703 			sprintf(stfp, " -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
704 				dir, X(a->ht), X(a->ht), X(a->wid)/2.);
705 			niceLine(stfp);
706 			break;
707 		case 2:	/* Closed with indented butt. */
708 			sprintf(stfp, " -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
709 				dir, 0.8 * X(a->ht), X(a->ht), X(a->wid)/2.);
710 			niceLine(stfp);
711 			break;
712 		case 3:	/* Closed with pointed butt. */
713 			sprintf(stfp, " -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
714 				dir, 1.2 * X(a->ht), X(a->ht), X(a->wid)/2.);
715 			niceLine(stfp);
716 			break;
717 		default:
718 			fprintf(stderr, "tkLine: unknown arrow type.\n");
719 			break;
720 		}
721 
722 	switch (s->cap_style) {
723 	case 0:	/* Butt (Tk default). */
724 		break;
725 	case 1:	/* Round. */
726 		sprintf(stfp, ", -capstyle => 'round'");
727 		niceLine(stfp);
728 		break;
729 	case 2: /* Projecting. */
730 		sprintf(stfp, ", -capstyle => 'projecting'");
731 		niceLine(stfp);
732 		break;
733 	default:
734 		fprintf(stderr, "tkLine: unknown cap style.\n");
735 		break;
736 	}
737 
738 	if (s->thickness != 1) {
739 		sprintf(stfp, ", -width => '%d'", s->thickness);
740 		niceLine(stfp);
741 	}
742 	if (s->pen_color != BLACK_COLOR && s->pen_color != DEFAULT) {
743 		sprintf(stfp, ", -fill => '#%6.6x'", s->pen_color);
744 		niceLine(stfp);
745 	}
746 	sprintf(stfp, ");\n");
747 	niceLine(stfp);
748 }
749 
750 /*
751  *   q u a d r a t i c S p l i n e ( )
752  */
753 
754 static void
quadraticSpline(double a1,double b1,double a2,double b2,double a3,double b3,double a4,double b4)755 quadraticSpline(double a1, double b1, double a2, double b2, double a3,
756 	double b3, double a4, double b4)
757 {
758 	char	stfp[64];
759 	double	x1, y1, x4, y4;
760 	double	xmid, ymid;
761 
762 	x1	 = a1; y1 = b1;
763 	x4	 = a4; y4 = b4;
764 	xmid	 = (a2 + a3)/2.0;
765 	ymid	 = (b2 + b3)/2.0;
766 	if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD) {
767 		sprintf(stfp, "'%.4fi', '%.4fi',\n", xmid, ymid);
768 		niceLine(stfp);
769 	} else {
770 		quadraticSpline(x1, y1, ((x1+a2)/2.0), ((y1+b2)/2.0),
771 			((3.0*a2+a3)/4.0), ((3.0*b2+b3)/4.0), xmid, ymid);
772 	    }
773 
774 	if (fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
775 		sprintf(stfp, "'%.4fi', '%.4fi', ", x4, y4);
776 		niceLine(stfp);
777 	} else {
778 		quadraticSpline(xmid, ymid, ((a2+3.0*a3)/4.0),
779 			((b2+3.0*b3)/4.0), ((a3+x4)/2.0), ((b3+y4)/2.0),
780 			x4, y4);
781 	}
782 }
783 
784 /*
785  *   g e n p T k C t l S p l i n e ( )
786  */
787 
788 static void
genptk_ctlSpline(F_spline * s)789 genptk_ctlSpline(F_spline *s)
790 {
791 	char	dir[8], stfp[1024];
792 	double	cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
793 	double	x1, y1, x2, y2;
794 	F_arrow	*a;
795 	F_point	*p;
796 
797 	p	 = s->points;
798 	x1	 = p->x/ppi;
799 	y1	 = p->y/ppi;
800 	p	 = p->next;
801 	x2	 = p->x/ppi;
802 	y2	 = p->y/ppi;
803 	cx1	 = (x1 + x2)/2.0;
804 	cy1	 = (y1 + y2)/2.0;
805 	cx2	 = (x1 + 3.0*x2)/4.0;
806 	cy2	 = (y1 + 3.0*y2)/4.0;
807 
808 	if (closed_spline(s)) {
809 		sprintf(stfp, "%s->createPolygon(qw/%.4f %.4f/,", canvas, cx1, cy1);
810 		niceLine(stfp);
811 	} else {
812 		sprintf(stfp, "%s->createLine(", canvas);
813 		niceLine(stfp);
814 	}
815 
816 	for (p = p->next; p != NULL; p = p->next) {
817 		x1	 = x2;
818 		y1	 = y2;
819 		x2	 = p->x/ppi;
820 		y2	 = p->y/ppi;
821 		cx3	 = (3.0*x1 + x2)/4.0;
822 		cy3	 = (3.0*y1 + y2)/4.0;
823 		cx4	 = (x1 + x2)/2.0;
824 		cy4	 = (y1 + y2)/2.0;
825 		quadraticSpline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
826 		cx1	 = cx4;
827 		cy1	 = cy4;
828 		cx2	 = (x1 + 3.0*x2)/4.0;
829 		cy2	 = (y1 + 3.0*y2)/4.0;
830 	}
831 	x1	 = x2;
832 	y1	 = y2;
833 	p	 = s->points->next;
834 	x2	 = p->x/ppi;
835 	y2	 = p->y/ppi;
836 	cx3	 = (3.0*x1 + x2)/4.0;
837 	cy3	 = (3.0*y1 + y2)/4.0;
838 	cx4	 = (x1 + x2)/2.0;
839 	cy4	 = (y1 + y2)/2.0;
840 	if (closed_spline(s))
841 		quadraticSpline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
842 
843 	if (closed_spline(s)) {
844 		if (s->pen_color == NONE) {
845 			sprintf(stfp, " -outline => undef,");
846 			niceLine(stfp);
847 		} else {
848 			sprintf(stfp, " -outline => '#%6.6x',", s->pen_color);
849 			niceLine(stfp);
850 		}
851 		if (s->fill_color == NONE) {
852 			sprintf(stfp, " -fill => undef,");
853 			niceLine(stfp);
854 		} else {
855 			sprintf(stfp, " -fill => '#%6.6x',", s->fill_color);
856 			niceLine(stfp);
857 		}
858 		if (s->fill_style != NONE) {
859 			sprintf(stfp, " -stipple => \"\\@%s\",",
860 				stippleFilename(s->fill_style));
861 			niceLine(stfp);
862 		}
863 		if (s->thickness != 1) {
864 			sprintf(stfp, " -width => '%d'", s->thickness);
865 			niceLine(stfp);
866 		}
867 	} else {
868 
869 		a = NULL;
870 		if (s->for_arrow && !s->back_arrow) {
871 			a = s->for_arrow;
872 			strcpy(dir, "last");
873 		} else if (!s->for_arrow && s->back_arrow) {
874 			a = s->back_arrow;
875 			strcpy(dir, "first");
876 		} else if (s->for_arrow && s->back_arrow) {
877 			a = s->back_arrow;
878 			strcpy(dir, "both");
879 		}
880 		if (a)
881 			switch (a->type) {
882 			case 0:	/* Stick type. */
883 				sprintf(stfp, " -arrow => '%s', -arrowshape =>"
884 					"[0, '%fi', '%fi'],",
885 					dir, X(a->ht), X(a->wid)/2.);
886 				niceLine(stfp);
887 				fprintf(stderr, "Warning: stick arrows do not "
888 					"work well in Tk.\n");
889 				break;
890 			case 1:	/* Closed triangle. */
891 				sprintf(stfp, " -arrow => '%s', -arrowshape =>"
892 					"['%fi', '%fi', '%fi']",
893 					dir, X(a->ht), X(a->ht), X(a->wid)/2.);
894 				niceLine(stfp);
895 				break;
896 			case 2:	/* Closed with indented butt. */
897 				sprintf(stfp, " -arrow => '%s', -arrowshape =>"
898 					"['%fi', '%fi', '%fi']", dir, 0.8 * X(a->ht),
899 					X(a->ht), X(a->wid)/2.);
900 				niceLine(stfp);
901 				break;
902 			case 3:	/* Closed with pointed butt. */
903 				sprintf(stfp, " -arrow => %s, -arrowshape =>"
904 					"['%fi', '%fi', '%fi']", dir, 1.2 * X(a->ht),
905 					X(a->ht), X(a->wid)/2.);
906 				niceLine(stfp);
907 				break;
908 			default:
909 				fprintf(stderr, "tkLine: unknown arrow "
910 					"type.\n");
911 				break;
912 			}
913 
914 		switch (s->cap_style) {
915 		case 0:	/* Butt (Tk default). */
916 			break;
917 		case 1:	/* Round. */
918 			sprintf(stfp, " -capstyle => 'round',");
919 			niceLine(stfp);
920 			break;
921 		case 2: /* Projecting. */
922 			sprintf(stfp, " -capstyle => 'projecting',");
923 			niceLine(stfp);
924 			break;
925 		default:
926 			fprintf(stderr, "tkLine: unknown cap style.\n");
927 			break;
928 		}
929 
930 		if (s->thickness != 1) {
931 			sprintf(stfp, " -width => '%d',", s->thickness);
932 			niceLine(stfp);
933 		}
934 		if (s->pen_color != BLACK_COLOR && s->pen_color != DEFAULT) {
935 			sprintf(stfp, " -fill => '#%6.6x',", s->pen_color);
936 			niceLine(stfp);
937 		}
938 	}
939 	sprintf(stfp, ");\n");
940 }
941 
942 /*
943  *   g e n p T k S p l i n e ( )
944  */
945 
946 void
genptk_spline(F_spline * s)947 genptk_spline(F_spline *s)
948 {
949 	/* print any comments prefixed with "#" */
950 	print_comments("# ",s->comments, "");
951 
952 	if (int_spline(s))
953 		genptk_itpSpline(s);
954 	else
955 		genptk_ctlSpline(s);
956 }
957 
958 /*
959  *   d r a w S h a p e ( )
960  *
961  *   This routine is way too dependent on hardcoded fill_style values.
962  *   Because so many other gen*.c routines use them, though, I'm too
963  *   lazy to go through all the code.
964  */
965 
966 static void
drawShape(void (* ptkShape)(),void * p,int thickness,int penColor,int fillColor,int fillStyle)967 drawShape(void (*ptkShape)(), void *p, int thickness, int penColor,
968 	int fillColor, int fillStyle)
969 {
970 	/* Draw filled and/or stippled region enclosed by shape. */
971 	if (fillStyle != UNFILLED) {
972 		switch (fillColor) {
973 		case DEFAULT:
974 		case BLACK_COLOR:
975 		case WHITE_COLOR:
976 			if (fillStyle < 20) {
977 				/* Draw underlying shape. */
978 				ptkShape(p, NONE, rgbColorVal(fillColor)
979 					 ^ 0xffffff, NONE, 0);
980 				/* Draw stipple pattern in fill color. */
981 				ptkShape(p, NONE, rgbColorVal(fillColor),
982 					 fillStyle, 0);
983 			} else if (fillStyle == 20) {
984 				/* Draw underlying shape. */
985 				ptkShape(p, NONE, rgbColorVal(fillColor),
986 					 NONE, 0);
987 			} else if (fillStyle <= 40) {
988 				/* Should never get here... */
989 				fprintf(stderr, "drawShape: b&w error.\n");
990 			} else {
991 				/* Draw underlying shape with fill color. */
992 				ptkShape(p, NONE, rgbColorVal(fillColor),
993 					 NONE, 0);
994 				/* Draw fill pattern with pen color. */
995 				ptkShape(p, NONE, rgbColorVal(penColor),
996 					 fillStyle, 0);
997 			}
998 			break;
999 		default:
1000 			if (fillStyle < 20) {
1001 				/* First, fill region with black. */
1002 				ptkShape(p, NONE, 0x000000, NONE, 0);
1003 				/* Then, stipple pattern in fill color. */
1004 				ptkShape(p, NONE, rgbColorVal(fillColor),
1005 					 fillStyle, 0);
1006 			} else if (fillStyle == 20) {
1007 				/* Full saturation of fill color. */
1008 				ptkShape(p, NONE, rgbColorVal(fillColor),
1009 					 NONE, 0);
1010 			} else if (fillStyle < 40) {
1011 				/* First, fill region with fill color. */
1012 				ptkShape(p, NONE, rgbColorVal(fillColor),
1013 					NONE, 0);
1014 				/* Then, draw stipple pattern in white. */
1015 				ptkShape(p, NONE, 0xffffff, fillStyle-20, 0);
1016 			} else if (fillStyle == 40) {
1017 				/* Maximally tinted: white. */
1018 				ptkShape(p, NONE, 0xffffff, NONE, 0);
1019 			} else {
1020 				/* Draw underlying shape with fill color. */
1021 				ptkShape(p, NONE, rgbColorVal(fillColor),
1022 					 NONE, 0);
1023 				/* Draw fill pattern with pen color. */
1024 				ptkShape(p, NONE, rgbColorVal(penColor),
1025 					 fillStyle, 0);
1026 			}
1027 			break;
1028 		}
1029 	}
1030 
1031 	/* Finally draw shape itself. */
1032 	if (thickness > 0)
1033 		ptkShape(p, rgbColorVal(penColor), NONE, NONE, thickness/15);
1034 }
1035 
1036 /*
1037  *   p t k A r c ( )
1038  *
1039  *   Generate the Tk canvas item and options for an arc.  Certain
1040  *   assumptions are made regarding filling (using Tk chord style)
1041  *   or unfilled (Tk arc style) to mimic xfig.
1042  */
1043 
1044 void
ptkArc(void * shape,unsigned int outlineColor,unsigned int fillColor,unsigned int fillPattern,int thickness)1045 ptkArc(void *shape, unsigned int outlineColor, unsigned int fillColor,
1046 	unsigned int fillPattern, int thickness)
1047 {
1048 	char	stfp[1024];
1049 	double	cx, cy,	/* Center of circle containing arc. */
1050 		sx, sy,	/* Start point of arc. */
1051 		ex, ey,	/* Stop point of arc. */
1052 		angle1, angle2, extent, radius, startAngle;
1053 	F_arc	*a;
1054 
1055 	a = (F_arc *) shape;
1056 	cx = X(a->center.x);		/* Center. */
1057 	cy = Y(a->center.y);
1058 	sx = X(a->point[0].x) - cx;	/* Start point. */
1059 	sy = cy - Y(a->point[0].y);
1060 	ex = X(a->point[2].x) - cx;	/* End point. */
1061 	ey = cy - Y(a->point[2].y);
1062 
1063 	radius = sqrt(sy*sy + sx*sx);
1064 	angle1 = atan2(sy, sx) * 180.0 / M_PI;
1065 	if (angle1 < 0.)
1066 		angle1 += 360.;
1067 	angle2 = atan2(ey, ex) * 180.0 / M_PI;
1068 	if (angle2 < 0.)
1069 		angle2 += 360.;
1070 
1071 	if (a->direction == 1) {	/* Counter-clockwise. */
1072 		startAngle = angle1;
1073 		extent = angle2 - angle1;
1074 	} else {			/* Clockwise. */
1075 		startAngle = angle2;
1076 		extent = angle1 - angle2;
1077 	}
1078 	if (extent < 0.)		/* Sweep of arc. */
1079 		extent += 360.;
1080 
1081 	sprintf(stfp, "%s->createArc(", canvas);
1082 	niceLine(stfp);
1083 	/* Coords of bounding rectangle. */
1084 	sprintf(stfp, "qw/%.3fi %.3fi %.3fi %.3fi",
1085 		cx-radius, cy-radius, cx+radius, cy+radius);
1086 	niceLine(stfp);
1087 	/* Start angle in degrees and its extent in degrees. */
1088 	sprintf(stfp, " -start %lf -extent %lf/", startAngle, extent);
1089 	niceLine(stfp);
1090 
1091 	if (outlineColor == NONE)
1092 		sprintf(stfp, ", -outline => undef");
1093 	else
1094 		sprintf(stfp, ", -outline => '#%6.6x'", outlineColor);
1095 	niceLine(stfp);
1096 
1097 	switch (a->type) {
1098 	case T_OPEN_ARC:
1099 		if (fillColor == NONE)
1100 			sprintf(stfp, ", -style => 'arc', -fill => undef");
1101 		else
1102 			sprintf(stfp, ", -style => 'chord', -fill => '#%6.6x'", fillColor);
1103 		niceLine(stfp);
1104 		break;
1105 	case T_PIE_WEDGE_ARC:
1106 		if (fillColor == NONE)
1107 			sprintf(stfp, ", -style => 'pieslice', -fill => undef");
1108 		else
1109 			sprintf(stfp, ", -style => 'pieslice', -fill => '#%6.6x'",
1110 				fillColor);
1111 		niceLine(stfp);
1112 		break;
1113 	default:
1114 		fprintf(stderr, "ptkArc: unknown arc type.\n");
1115 		break;
1116 	}
1117 
1118 	if (fillPattern != NONE) {
1119 		sprintf(stfp, ", -stipple => \"\\@%s\"", stippleFilename(fillPattern));
1120 		niceLine(stfp);
1121 	}
1122 	if (thickness != 1) {
1123 		sprintf(stfp, ", -width => '%d'", thickness);
1124 		niceLine(stfp);
1125 	}
1126 
1127 	sprintf(stfp, ");\n");
1128 	niceLine(stfp);
1129 }
1130 
1131 /*
1132  *   p t k E l l i p s e ( )
1133  */
1134 
1135 void
ptkEllipse(void * shape,unsigned int outlineColor,unsigned int fillColor,unsigned int fillPattern,int thickness)1136 ptkEllipse(void *shape, unsigned int outlineColor, unsigned int fillColor,
1137 	unsigned int fillPattern, int thickness)
1138 {
1139 	char		stfp[1024];
1140 	F_ellipse	*e;
1141 
1142 	e = (F_ellipse *) shape;
1143 	sprintf(stfp, "%s->createOval(qw/%lfi %lfi %lfi %lfi/",
1144 		canvas,
1145 		X(e->center.x - e->radiuses.x),
1146 		Y(e->center.y - e->radiuses.y),
1147 		X(e->center.x + e->radiuses.x),
1148 		Y(e->center.y + e->radiuses.y));
1149 	niceLine(stfp);
1150 
1151 	if (outlineColor == NONE)
1152 		sprintf(stfp, ", -outline => undef");
1153 	else
1154 		sprintf(stfp, ", -outline => '#%6.6x'", outlineColor);
1155 	niceLine(stfp);
1156 
1157 	if (fillColor == NONE)
1158 		sprintf(stfp, ", -fill => undef");
1159 	else
1160 		sprintf(stfp, ", -fill => '#%6.6x'", fillColor);
1161 	niceLine(stfp);
1162 
1163 	if (fillPattern != NONE) {
1164 		sprintf(stfp, ", -stipple => \"\\@%s\"", stippleFilename(fillPattern));
1165 		niceLine(stfp);
1166 	}
1167 
1168 	if (thickness != 1) {
1169 		sprintf(stfp, ", -width => '%d'", thickness);
1170 		niceLine(stfp);
1171 	}
1172 
1173 	sprintf(stfp, ");\n");
1174 	niceLine(stfp);
1175 }
1176 
1177 /*
1178  *   p t k L i n e ( )
1179  */
1180 
1181 static void
ptkLine(void * shape,unsigned int penColor,unsigned int fillColor,unsigned int fillPattern,int thickness)1182 ptkLine(void * shape, unsigned int penColor, unsigned int fillColor,
1183 	unsigned int fillPattern, int thickness)
1184 {
1185 	char		dir[8], stfp[1024];
1186 	extern char	*canvas;	/* Tk canvas name. */
1187 	F_arrow		*a;
1188 	F_line		*l;
1189 	F_point		*p, *q;
1190 
1191 	l = (F_line *) shape;
1192 	p = l->points;
1193 	q = p->next;
1194 
1195 	if (q == NULL) {
1196 		/* Degenerate line (single point). */
1197 		sprintf(stfp, "%s->createLine(qw/%lfi %lfi %lfi %lfi/",
1198 			canvas, X(p->x), Y(p->y), X(p->x), Y(p->y));
1199 		niceLine(stfp);
1200 	} else {
1201 		sprintf(stfp, "%s->createLine(", canvas);
1202 		niceLine(stfp);
1203 		sprintf(stfp, " '%lfi', '%lfi'", X(p->x), Y(p->y));
1204 		niceLine(stfp);
1205 		for ( /* No op. */ ; q != NULL; q = q->next) {
1206 			sprintf(stfp, ", '%lfi', '%lfi'", X(q->x), Y(q->y));
1207 			niceLine(stfp);
1208 		}
1209 	}
1210 
1211 	switch (l->style) {
1212 	case -1:/* Default. */
1213 	case 0:	/* Solid line. */
1214 		break;
1215 	case 1:	/* Dashed line. */
1216 	case 2:	/* Dotted line. */
1217 	case 3:	/* Dash-dotted line. */
1218 	case 4:	/* Dash-double-dotted line. */
1219 	case 5:	/* Dash-triple-dotted line. */
1220 	default:
1221 		fprintf(stderr, "ptkLine: only solid line styles supported.\n");
1222 		break;
1223 	}
1224 
1225 	a = NULL;
1226 	if (l->for_arrow && !l->back_arrow) {
1227 		a = l->for_arrow;
1228 		strcpy(dir, "last");
1229 	} else if (!l->for_arrow && l->back_arrow) {
1230 		a = l->back_arrow;
1231 		strcpy(dir, "first");
1232 	} else if (l->for_arrow && l->back_arrow) {
1233 		a = l->back_arrow;
1234 		strcpy(dir, "both");
1235 	}
1236 	if (a)
1237 		switch (a->type) {
1238 		case 0:	/* Stick type. */
1239 			sprintf(stfp, ", -arrow => '%s', -arrowshape => [0, '%fi', '%fi']",
1240 				dir, X(a->ht), X(a->wid)/2.);
1241 			niceLine(stfp);
1242 			fprintf(stderr, "Warning: stick arrows do not "
1243 				"work well in Tk.\n");
1244 			break;
1245 		case 1:	/* Closed triangle. */
1246 			sprintf(stfp, ", -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
1247 				dir, X(a->ht), X(a->ht), X(a->wid)/2.);
1248 			niceLine(stfp);
1249 			break;
1250 		case 2:	/* Closed with indented butt. */
1251 			sprintf(stfp, ", -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
1252 				dir, 0.8 * X(a->ht), X(a->ht), X(a->wid)/2.);
1253 			niceLine(stfp);
1254 			break;
1255 		case 3:	/* Closed with pointed butt. */
1256 			sprintf(stfp, ", -arrow => '%s', -arrowshape => ['%fi', '%fi', '%fi']",
1257 				dir, 1.2 * X(a->ht), X(a->ht), X(a->wid)/2.);
1258 			niceLine(stfp);
1259 			break;
1260 		default:
1261 			fprintf(stderr, "ptkLine: unknown arrow type.\n");
1262 			break;
1263 		}
1264 
1265 	switch (l->join_style) {
1266 	case 0:	/* Miter (Tk default). */
1267 		break;
1268 	case 1:	/* Round. */
1269 		sprintf(stfp, ", -joinstyle => 'round'");
1270 		niceLine(stfp);
1271 		break;
1272 	case 2:	/* Bevel. */
1273 		sprintf(stfp, ", -joinstyle => 'bevel'");
1274 		niceLine(stfp);
1275 		break;
1276 	default:
1277 		fprintf(stderr, "ptkLine: unknown join style.\n");
1278 		break;
1279 	}
1280 
1281 	switch (l->cap_style) {
1282 	case 0:	/* Butt (Tk default). */
1283 		break;
1284 	case 1:	/* Round. */
1285 		sprintf(stfp, ", -capstyle => 'round'");
1286 		niceLine(stfp);
1287 		break;
1288 	case 2: /* Projecting. */
1289 		sprintf(stfp, ", -capstyle => 'projecting'");
1290 		niceLine(stfp);
1291 		break;
1292 	default:
1293 		fprintf(stderr, "ptkLine: unknown cap style.\n");
1294 		break;
1295 	}
1296 
1297 	if (thickness != 1) {
1298 		sprintf(stfp, ", -width => %d", thickness);
1299 		niceLine(stfp);
1300 	}
1301 	if (penColor != BLACK_COLOR && penColor != DEFAULT) {
1302 		sprintf(stfp, ", -fill => '#%6.6x'", penColor);
1303 		niceLine(stfp);
1304 	}
1305 	sprintf(stfp, ");\n");
1306 	niceLine(stfp);
1307 }
1308 
1309 /*
1310  *   p t k P o l y g o n ( )
1311  */
1312 
1313 static void
ptkPolygon(void * shape,unsigned int outlineColor,unsigned int fillColor,unsigned int fillPattern,int thickness)1314 ptkPolygon(void * shape, unsigned int outlineColor, unsigned int fillColor,
1315 	unsigned int fillPattern, int thickness)
1316 {
1317 	char		stfp[1024];
1318 	extern char	*canvas;	/* Tk canvas name. */
1319 	F_point 	*p, *q;
1320 	int		pts;
1321 
1322 	p = (F_point *) shape;
1323 
1324 	q = p->next;
1325 	for (pts = 0; q != NULL; q = q->next) {
1326 		if (++pts == 3)
1327 			break;
1328 	}
1329 	if (pts < 3)
1330 		return;	/* Bail out if it's not a polygon. */
1331 
1332 	q = p->next;
1333 	sprintf(stfp, "%s->createPolygon(", canvas);
1334 	niceLine(stfp);
1335 	sprintf(stfp, "'%lfi', '%lfi'", X(p->x), Y(p->y));
1336 	niceLine(stfp);
1337 	for ( /* No op. */ ; q != NULL; q = q->next) {
1338 		sprintf(stfp, ", '%lfi', '%lfi'", X(q->x), Y(q->y));
1339 		niceLine(stfp);
1340 	}
1341 
1342 	if (outlineColor == NONE)
1343 		sprintf(stfp, ", -outline => undef");
1344 	else
1345 		sprintf(stfp, ", -outline => '#%6.6x'", outlineColor);
1346 	niceLine(stfp);
1347 
1348 	if (fillColor == NONE)
1349 		sprintf(stfp, ", -fill => undef");
1350 	else
1351 		sprintf(stfp, ", -fill => '#%6.6x'", fillColor);
1352 	niceLine(stfp);
1353 
1354 	if (fillPattern != NONE) {
1355 		sprintf(stfp, ", -stipple => \"\\@%s\"", stippleFilename(fillPattern));
1356 		niceLine(stfp);
1357 	}
1358 
1359 	if (thickness != 1) {
1360 		sprintf(stfp, ", -width => '%d'", thickness);
1361 		niceLine(stfp);
1362 	}
1363 
1364 	sprintf(stfp, ");\n");
1365 	niceLine(stfp);
1366 }
1367 
1368 /*
1369  *   r g b C o l o r V a l ( )
1370  *
1371  *   Given an indexi into either the standard color list or into the
1372  *   user defined color list, return the hex RGB value of the color.
1373  */
1374 
1375 static unsigned int
rgbColorVal(int colorIndex)1376 rgbColorVal(int colorIndex)
1377 {
1378 	extern User_color	user_colors[];
1379 	unsigned int	rgb;
1380 	static unsigned int	rgbColors[NUM_STD_COLS] = {
1381 		0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff,
1382 		0xffff00, 0xffffff, 0x00008f, 0x0000b0, 0x0000d1, 0x87cfff,
1383 		0x008f00, 0x00b000, 0x00d100, 0x008f8f, 0x00b0b0, 0x00d1d1,
1384 		0x8f0000, 0xb00000, 0xd10000, 0x8f008f, 0xb000b0, 0xd100d1,
1385 		0x803000, 0xa14000, 0xb46100, 0xff8080, 0xffa1a1, 0xffbfbf,
1386 		0xffe0e0, 0xffd600
1387 	};
1388 
1389 	if (colorIndex == DEFAULT)
1390 		rgb = rgbColors[0];
1391 	else if (colorIndex < NUM_STD_COLS)
1392 		rgb = rgbColors[colorIndex];
1393 	else
1394 		rgb = ((user_colors[colorIndex-NUM_STD_COLS].r & 0xff) << 16)
1395 			| ((user_colors[colorIndex-NUM_STD_COLS].g & 0xff) << 8)
1396 			| (user_colors[colorIndex-NUM_STD_COLS].b & 0xff);
1397 	return rgb;
1398 }
1399 
1400 /*
1401  *   s t i p p l e F i l e n a m e ( )
1402  *
1403  *   Given xfig index number, return the filename of the corresponding
1404  *   stipple pattern.  Tk requires the bitmap to be in a file.
1405  */
1406 
1407 static char *
stippleFilename(int patternIndex)1408 stippleFilename(int patternIndex)
1409 {
1410 	static char *	fillNames[63] = {
1411 		"sp0.bmp", "sp1.bmp", "sp2.bmp", "sp3.bmp", "sp4.bmp",
1412 		"sp5.bmp", "sp6.bmp", "sp7.bmp", "sp8.bmp", "sp9.bmp",
1413 		"sp10.bmp", "sp11.bmp", "sp12.bmp", "sp13.bmp", "sp14.bmp",
1414 		"sp15.bmp", "sp16.bmp", "sp17.bmp", "sp18.bmp", "sp19.bmp",
1415 		"sp20.bmp",
1416 
1417 		"", "", "", "", "", "", "", "", "", "",
1418 		"", "", "", "", "", "", "", "", "", "",
1419 
1420 		"left30.bmp", "right30.bmp", "crosshatch30.bmp",
1421 		"left45.bmp", "right45.bmp", "crosshatch45.bmp",
1422 		"bricks.bmp", "vert_bricks.bmp", "horizontal.bmp",
1423 		"vertical.bmp", "crosshatch.bmp", "leftshingle.bmp",
1424 		"rightshingle.bmp", "vert_leftshingle.bmp",
1425 		"vert_rightshingle.bmp", "fishscales.bmp",
1426 		"small_fishscales.bmp", "circles.bmp", "hexagons.bmp",
1427 		"octagons.bmp", "horiz_saw.bmp", "vert_saw.bmp"
1428 	};
1429 	extern char	*xbmPathVar;	/* Global. */
1430 	static char	path[256];
1431 
1432 	/* XXX something like File::Spec would be better, but this is
1433 	   sufficient for Unix and Win32 */
1434 	sprintf(path, "%s/%s",
1435 		xbmPathVar, fillNames[patternIndex]);
1436 	return path;
1437 }
1438 
1439 /*
1440  *   n i c e L i n e ( )
1441  *
1442  *   Instead of directly calling fprintf()'s, this routine is used so
1443  *   that lines are printed that are 80 characters long, give or take
1444  *   a handful.  Otherwise - spline routines in particular - will generate
1445  *   humongously long lines that are a pain in the butt to edit...
1446  */
1447 
1448 static void
niceLine(char * s)1449 niceLine(char *s)
1450 {
1451 	extern FILE	*tfp;	/* File descriptor of Tk file. */
1452 	int		i, len;
1453 	static int	inQuote = 0;
1454 	static int	pos = 0;
1455 
1456 	len = strlen(s);
1457 	for (i = 0; i < len; i++) {
1458 		if (s[i] == '"') {
1459 			inQuote ^= 1;	/* Flip between 0/1. */
1460 			putc(s[i], tfp);
1461 			pos++;
1462 		} else if (!inQuote && (pos > 80) && (s[i] == ' ')) {
1463 			fprintf(tfp, " \n  ");
1464 			pos = 2;
1465 		} else {
1466 			putc(s[i], tfp);
1467 			if (s[i] == '\n')
1468 				pos = 0;
1469 			else
1470 				pos++;
1471 		}
1472 	}
1473 }
1474 
1475 /*
1476  *   d e v _ p t k
1477  *
1478  *   The wrapper used by fig2dev to
1479  *   isolate the device specific details.
1480  */
1481 
1482 struct driver   dev_ptk = {
1483 	genptk_option,
1484 	genptk_start,
1485 	gendev_null,
1486 	genptk_arc,
1487 	genptk_ellipse,
1488 	genptk_line,
1489 	genptk_spline,
1490 	genptk_text,
1491 	genptk_end,
1492 	INCLUDE_TEXT
1493 };
1494 
1495