1 /*
2  * TransFig: Facility for Translating Fig code
3  * Copyright (c) 1991 by Micah Beck
4  * Copyright (c) 1988 by Conrad Kwok
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  * genpstricks.c: pstricks driver for fig2dev
21  * Copyright (c) 2004, 2005, 2006, 2007, 2008 by Eugene Ressler
22  * Acknowledgement: code for font manipulation is modeled closely after
23  *   the epic driver by Conrad Kwok.
24  *
25  * Changes:
26  *   V1.2:
27  *   27 May 2010 - Added new arrowhead styles and various gcc warning fixes.
28  */
29 
30 #define PST_DRIVER_VERSION 1.2
31 
32 #include "fig2dev.h"
33 #include "object.h"
34 #include "texfonts.h"
35 
36 /**********************************************************************/
37 /* utility functions                                                  */
38 /**********************************************************************/
39 
40 #define _STRINGIFY(X) #X
41 #define STRINGIFY(S) _STRINGIFY(S)
42 #define ARRAY_SIZE(A) ((int)(sizeof (A) / sizeof (A)[0]))
43 #define bit(N) (1 << (N))
44 #define PI (3.1415926535897932384626433832795)
45 
46 /* format used for double coordinates */
47 #define DBL "%.4lf"
48 
49 /* return (double) centimeters for xfig integer distances */
50 #define cm_from_80ths(Val) \
51   ((double)((Val) * (2.54 / 80.0)))
52 
53 #define cm_from_1200ths(Val)  \
54   ((double)((Val) / ppi * 2.54))
55 
56 /* compute min/max of double pair */
57 static double
min_dbl(a,b)58 min_dbl(a, b)
59      double a, b;
60 {
61   return a < b ? a : b;
62 }
63 
64 static double
max_dbl(a,b)65 max_dbl(a, b)
66      double a, b;
67 {
68   return a > b ? a : b;
69 }
70 
71 static double
sqr_dbl(x)72 sqr_dbl(x)
73      double x;
74 {
75   return x * x;
76 }
77 
78 /* return absolute value of int */
79 static int
abs_int(x)80 abs_int(x)
81      int x;
82 {
83   return x < 0 ? -x : x;
84 }
85 
86 /* compute min/max of int pair */
87 static int
min_int(a,b)88 min_int(a, b)
89      int a, b;
90 {
91   return a < b ? a : b;
92 }
93 
94 static int
max_int(a,b)95 max_int(a, b)
96      int a, b;
97 {
98   return a > b ? a : b;
99 }
100 
101 /* fold a new value into an accumulated integer min/max */
102 static void
fold_min_int(acc,val)103 fold_min_int(acc, val)
104      int *acc, val;
105 {
106   if (val < *acc)
107     *acc = val;
108 }
109 
110 static void
fold_max_int(acc,val)111 fold_max_int(acc, val)
112      int *acc, val;
113 {
114   if (val > *acc)
115     *acc = val;
116 }
117 
118 /* return a roman numeral for a given integer */
119 static char*
roman_numeral_from_int(char * rn,unsigned n)120 roman_numeral_from_int(char *rn, unsigned n)
121 {
122   static char rn_digits[][4] = {
123     "M", "CM", "D", "CD",
124     "C", "XC", "L", "XL",
125     "X", "IX", "V", "IV",
126     "m", "cm", "d", "cd",
127     "c", "xc", "l", "xl",
128     "x", "ix", "v", "iv",
129     "i"
130   };
131   static unsigned rn_divs[] = {
132     1000000, 900000, 500000, 400000,
133     100000,  90000,  50000,  40000,
134     10000,   9000,   5000,   4000,
135     1000,    900,    500,    400,
136     100,     90,     50,     40,
137     10,      9,      5,      4,
138     1
139   };
140   unsigned i;
141 
142   rn[0] = '\0';
143   for (i = 0; i < ARRAY_SIZE(rn_divs); i++)
144     while (n >= rn_divs[i]) {
145       n -= rn_divs[i];
146       strcat(rn, rn_digits[i]);
147     }
148   return rn;
149 }
150 
151 void *
xmalloc(n)152 xmalloc(n)
153      unsigned n;
154 {
155   void *p = (void*)malloc(n);
156   if (!p) {
157     fprintf(stderr, "out of memory.\n");
158     exit(1);
159   }
160   return p;
161 }
162 
163 char *
xstrdup(s)164 xstrdup(s)
165      char *s;
166 {
167   char *c = (char*)xmalloc(strlen(s) + 1);
168   strcpy(c, s);
169   return c;
170 }
171 
172 /* traverse an xfig point list and return its bounding box */
173 static void
find_bounding_box_of_points(llx,lly,urx,ury,pts)174 find_bounding_box_of_points(llx, lly, urx, ury, pts)
175      int *llx, *lly, *urx, *ury;
176      F_point *pts;
177 {
178   F_point *p;
179 
180   if (!pts) {
181     *llx = *lly = *urx = *ury = 0;
182     return;
183   }
184 
185   *llx = *urx = pts->x;
186   *lly = *ury = pts->y;
187   for (p = pts->next; p; p = p->next) {
188     fold_min_int(llx, p->x);
189     fold_min_int(lly, p->y);
190     fold_max_int(urx, p->x);
191     fold_max_int(ury, p->y);
192   }
193 }
194 
195 /* Xfig's coordinate system has y=0 at the top. These conversions make
196    coordinates bounding-box relative, which is good for generating
197    figures and full-page printouts. These functions fix that by
198    computing coordinates relative to the entire image bounding box.
199    They also incorporate user-specified margins.
200 
201    The llx and lly here are the global bounding box set in fig2dev.
202    They are misleadingly labeled as to which is bigger and smaller,
203    so the absolute values and min/maxes here are used to remove all
204    doubt. */
205 
206 static void
convert_xy_to_image_bb(x_bb,y_bb,x,y)207 convert_xy_to_image_bb(x_bb, y_bb, x, y)
208      int *x_bb, *y_bb, x, y;
209 {
210   /* llx and lly are global bounding box set by the reader */
211   *x_bb = x - min_int(llx, urx);
212   *y_bb = max_int(lly, ury) - y;
213 }
214 
215 static void
convert_xy_dbl_to_image_bb(x_bb,y_bb,x,y)216 convert_xy_dbl_to_image_bb(x_bb, y_bb, x, y)
217      double *x_bb, *y_bb, x, y;
218 {
219   *x_bb = x - min_int(llx, urx);
220   *y_bb = max_int(lly, ury) - y;
221 }
222 
223 static void
convert_pt_to_image_bb(pt_bb,pt)224 convert_pt_to_image_bb(pt_bb, pt)
225      F_point *pt_bb, *pt;
226 {
227   convert_xy_to_image_bb(&pt_bb->x, &pt_bb->y, pt->x, pt->y);
228 }
229 
230 static void
convert_pt_to_image_bb_in_place(pt)231 convert_pt_to_image_bb_in_place(pt)
232      F_point *pt;
233 {
234   convert_pt_to_image_bb(pt, pt);
235 }
236 
237 /* append a new string value onto a comma-separated list */
238 static void
append_to_comma_list(lst,val)239 append_to_comma_list(lst, val)
240      char **lst, *val;
241 {
242   if (!val || !val[0])
243     return;
244   if (**lst) {
245     *lst += strlen(*lst);
246     sprintf(*lst, ",%s", val);
247   }
248   else
249     strcpy(*lst, val);
250 }
251 
252 /* character predicates; string.h is too non-compatible */
253 int
is_digit(int ch)254 is_digit (int ch)
255 {
256   return '0' <= ch && ch <= '9';
257 }
258 
259 int
lower(int ch)260 lower (int ch)
261 {
262   return ('A' <= ch && ch <= 'Z') ? ch + ('a' - 'A') : ch;
263 }
264 
265 int
is_alpha(int ch)266 is_alpha(int ch)
267 {
268   return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
269 }
270 
271 /**********************************************************************/
272 /* warning system                                                     */
273 /**********************************************************************/
274 
275 #define W_DASH_DOT         0
276 #define W_DASH_DOTS        1
277 #define W_HOLLOW_ARROW     2
278 #define W_HATCH_PATTERN    3
279 #define W_OLD_SPLNE        4
280 #define W_PIC              5
281 #define W_GRID             6
282 #define W_ELLIPSE_HATCH    7
283 #define W_FN_CHARS         8
284 #define W_SLASH_CONVERT    9
285 #define W_UNK_PIC_TYPE     10
286 #define W_SYSTEM_EXEC      11
287 #define W_BMEPS_ERR        12
288 #define W_EPS_NEEDED       13
289 #define W_PIC_CONVERT      14
290 #define W_PS_FONT          15
291 #define W_PAGESCALE        16
292 #define W_UNIT             17
293 #define W_LINEJOIN         18
294 #define W_UNKNOWN_ARROW    19
295 #define W_NEW_ARROW        20
296 #define W_TERMINATOR_ARROW 21
297 
298 #define W_RTN_ERROR_FLAGS (bit(W_UNK_PIC_TYPE) | bit(W_SYSTEM_EXEC) | bit(W_BMEPS_ERR))
299 
300 static char *warnings[] = {
301   /* 0 */ "Dash-dot lines require pstricks patchlevel 15 of June 2004 or later.",
302   /* 1 */ "Dash-dot-dot lines require \\usepackage{pstricks-add}.",
303   /* 2 */ "Hollow arrows required \\usepackage{pstricks-add}.",
304   /* 3 */ "A hatch pattern not supported by pstricks has been converted to crosshatch.",
305   /* 4 */ "Driver never tested with old splines. Send examples to ressler@usma.edu.",
306   /* 5 */ "Picture requires \\usepackage{graphicx}",
307   /* 6 */ "Grid sizes are ignored by the pstricks driver. Default \\psgrid issued.",
308   /* 7 */ "Hatch pattern in rotated ellipse may be imprecisely converted.",
309   /* 8 */ "Spaces and special chars in picture file path are likely to cause problems.",
310   /* 9 */ "Backslashes were converted to slashes in picture file path.",
311   /*10 */ "Couldn't convert unknown picture type to eps. Known: eps, png, jpg, pnm, tif (exit code 17).",
312   /*11 */ "Could not execute bmeps to convert picture to eps (exit code 17).",
313   /*12 */ "BMEPS returned non-0 exit code; picture conversion to eps probably failed (exit code 17).",
314   /*13 */ "Non-EPS picture paths were converted to the eps image directory.",
315   /*14 */ "Pictures were converted and placed in the eps image directory.",
316   /*15 */ "Subsituted LaTeX font for PS.  Use -v to see details.",
317   /*16 */ "Image scaled to page.",
318   /*17 */ "Setting unit requires \\psset{unit=X}\\newlength\\unit\\setlength\\unit{X}.",
319   /*18 */ "Linejoins depend on PSTricks version. Use -t option to ensure compatibility.",
320   /*19 */ "Unknown arrow type. Simple (stick) arrow substituted.",
321   /*20 */ "New arrow type requires xfig 3.2.5a or newer.",
322   /*21 */ "One or more arrows have been approximated with a PSTricks style. Try -R 0.",
323 };
324 
325 /* set of warnings that will be printed once at the end of the run */
326 static unsigned warn_flags = 0;
327 static int rtn_val = 0;
328 
warn(flag)329 void warn(flag)
330      int flag;
331 {
332   warn_flags |= bit(flag);
333   if (bit(flag) & W_RTN_ERROR_FLAGS)
334     rtn_val = 17;
335 }
336 
issue_warnings(f)337 void issue_warnings(f)
338      FILE *f;
339 {
340   unsigned i, mask;
341 
342   if (warn_flags) {
343     fprintf(f, "IMPORTANT notes from pstricks driver:\n");
344     for (i = 0, mask = 1; mask; i++, mask <<= 1)
345       if (mask & warn_flags)
346         fprintf(f, "  %s\n", warnings[i]);
347   }
348 }
349 
350 /**********************************************************************/
351 /* rudimentary string tables                                          */
352 /**********************************************************************/
353 
354 typedef struct string_table_node_t {
355   struct string_table_node_t *next;
356   int n_refs;
357   union {
358     int i;
359     double dbl;
360     void *vp;
361   } val;
362   char str[1];
363 } STRING_TABLE_NODE;
364 
365 typedef struct string_table_t {
366   STRING_TABLE_NODE *head;
367   int n_str;
368 } STRING_TABLE;
369 
370 /* static tables do not need to be initialized */
371 
372 #ifdef UNUSED
373 
374 static void
init_string_table(tbl)375 init_string_table(tbl)
376      STRING_TABLE *tbl;
377 {
378   tbl->head = 0;
379   tbl->n_str = 0;
380 }
381 
382 #endif
383 
384 static STRING_TABLE_NODE*
string_lookup_val(tbl,str)385 string_lookup_val(tbl, str)
386      STRING_TABLE *tbl;
387      char *str;
388 {
389   STRING_TABLE_NODE *stn;
390 
391   for (stn = tbl->head; stn; stn = stn->next) {
392     if (strcmp(stn->str, str) == 0)
393       return stn;
394   }
395   return 0;
396 }
397 
398 static STRING_TABLE_NODE*
add_string(tbl,str)399 add_string(tbl, str)
400      char *str;
401      STRING_TABLE *tbl;
402 {
403   STRING_TABLE_NODE *stn;
404 
405   stn = string_lookup_val(tbl, str);
406   if (stn)
407     stn->n_refs++;
408   else {
409     stn = (STRING_TABLE_NODE*)xmalloc(sizeof(STRING_TABLE_NODE) + strlen(str));
410     stn->next = tbl->head;
411     stn->n_refs = 1;
412     memset(&stn->val, 0, sizeof stn->val);
413     strcpy(stn->str, str);
414     tbl->head = stn;
415     tbl->n_str++;
416   }
417   return stn;
418 }
419 
420 /**********************************************************************/
421 /* user options                                                       */
422 /**********************************************************************/
423 
424 /* configurable parameters */
425 static int Verbose = False;
426 
427 #define PM_PICTURE     0
428 #define PM_BARE        1
429 #define PM_PAGE        2
430 #define PM_SCALED_PAGE 3
431 static int Page_mode = PM_PICTURE;
432 
433 #define A_XFIG      0
434 #define A_PSTRICKS  1
435 #define A_DEFAULT   2
436 static int Arrows = A_XFIG;
437 
438 #define FH_FULL       0
439 #define FH_SIZE_ONLY  1
440 #define FH_NONE       2
441 static int Font_handling = FH_FULL;
442 
443 static int Pic_convert_p = 0;
444 static char Pic_convert_dir[260] = "";
445 static double X_margin = 0, Y_margin = 0;
446 static double Line_weight = 0.5;
447 
448 static struct {
449   double mag;
450   int size;
451 } ScaleTbl[5] = {
452   { 0.6667, 8 },
453   { 0.75  , 9 },
454   { 0.8333, 10 },
455   { 0.9167, 11 },
456   { 1.0   , 12 }
457 };
458 
459 #define MAX_PST_VERSION_STRING_SIZE 10
460 
461 typedef struct pst_version_t {
462   char str[MAX_PST_VERSION_STRING_SIZE];
463   char key[MAX_PST_VERSION_STRING_SIZE * 2];
464   unsigned n_updates;
465 } PST_VERSION;
466 
467 /* Version 1.20 added linejoin option, where pstverb was
468    needed previously. */
469 #define PST_LINEJOIN_VERSION 1.20
470 
471 /* This should be set to the latest version that
472    makes a difference for Sketch output. */
473 #define ASSUMED_PST_VERSION PST_LINEJOIN_VERSION
474 
475 PST_VERSION Pst_version[1];
476 #define LJ_PSTVERB    0
477 #define LJ_PSTOPTION  1
478 int Linejoin = LJ_PSTOPTION;
479 
480 /* Fill a version struct given a version string.  This is
481    a little fsm that recognizes digit+.digit+[suffix],
482    where suffix is a single letter and digit is 0-9. Keys
483    are zero-padded and .-justified so a simple strcmp
484    lets us compare versions for relative order. Zero
485    return means parse is successful.  Other values are
486    error codes. */
487 int
parse_pst_version(PST_VERSION * v,char * str)488 parse_pst_version (PST_VERSION *v, char *str)
489 {
490 #define M (sizeof v->key / 2)
491   int i = 0, iv = 0, i_minor = -1;
492 
493   v->n_updates++;
494   memset(v->key, '0', sizeof v->key);
495   memset(v->str, '\0', sizeof v->str);
496 
497   if (strlen(str) > sizeof v->str - 1) {
498     fprintf(stderr, "PSTricks version string too long");
499     return 1;
500   }
501 
502   if ( is_digit (str[i]) ) {
503     v->str[iv++] = str[i++];
504     goto B;
505   }
506   else {
507     fprintf(stderr, "bad character '%c' in PSTricks version", str[i]);
508     return 2;
509   }
510  B:
511   if ( is_digit (str[i]) ) {
512     v->str[iv++] = str[i++];
513     goto B;
514   }
515   else if ( str[i] == '.' ) {
516     memcpy (&v->key[M - i], v->str, i); /* save major in key */
517     v->str[iv++] = str[i++];
518     i_minor = iv; /* remember where minor version starts */
519     goto C;
520   }
521   else {
522     fprintf(stderr, "expected dot in PSTricks version");
523     return 3;
524   }
525  C:
526   if ( is_digit (str[i]) ) {
527     v->str[iv++] = str[i++];
528     goto D;
529   }
530   else {
531     fprintf(stderr, "expected digit after dot in PSTricks version");
532     return 4;
533   }
534  D:
535   if ( is_digit (str[i]) ) {
536     v->str[iv++] = str[i++];
537     goto D;
538   }
539   else if ( is_alpha(str[i]) ) {
540     v->str[iv++] = lower (str[i++]);
541     goto F;
542   }
543   else if ( str[i] == '\0' ) {
544     memcpy (&v->key[M], &v->str[i_minor], i - i_minor); /* save minor in key */
545     return 0; /* accept */
546   }
547   else {
548     fprintf(stderr, "expected digit or subversion letter in PSTricks version");
549     return 5;
550   }
551  F:
552   if ( str[i] == '\0' ) {
553     memcpy (&v->key[M], &v->str[i_minor], i - i_minor);
554     return 0; /* accept */
555   }
556   else {
557     fprintf(stderr, "expected end of PSTricks version, found '%c'", str[i]);
558     return 6;
559   }
560   return -1;
561 }
562 
563 void
init_pst_version(PST_VERSION * v)564 init_pst_version(PST_VERSION *v)
565 {
566   v->n_updates = 0;
567   parse_pst_version(v, STRINGIFY(ASSUMED_PST_VERSION));
568   v->n_updates = 0;
569 }
570 
571 void
pst_version_cpy(PST_VERSION * dst,PST_VERSION * src)572 pst_version_cpy(PST_VERSION *dst, PST_VERSION *src)
573 {
574   unsigned n_dst_updates = dst->n_updates;
575   *dst = *src;
576   dst->n_updates = n_dst_updates + 1;
577 }
578 
579 int
pst_version_cmp(PST_VERSION * a,PST_VERSION * b)580 pst_version_cmp(PST_VERSION *a, PST_VERSION *b)
581 {
582   return strncmp(a->key, b->key, sizeof a->key);
583 }
584 
genpstrx_option(opt,optarg)585 void genpstrx_option(opt, optarg)
586 	char opt;
587 	char *optarg;
588 {
589   int i, tmp_int;
590   double tmp_dbl;
591   char fn[256];
592   FILE *f;
593   PST_VERSION tmp_pst_version[1];
594 
595   /* set up default version and a temp */
596   init_pst_version(Pst_version);
597   init_pst_version(tmp_pst_version);
598 
599   switch (opt) {
600 
601   case 'f':
602     for (i = 1; i <= MAX_FONT; i++)
603       if (strcmp(optarg, texfontnames[i]) == 0)
604         break;
605     if (i > MAX_FONT)
606       fprintf(stderr, "warning: non-standard font name %s ignored\n", optarg);
607     else {
608       texfontnames[0] = texfontnames[i];
609 #ifdef NFSS
610       texfontfamily[0] = texfontfamily[i];
611       texfontseries[0] = texfontseries[i];
612       texfontshape[0]  = texfontshape[i];
613 #endif
614     }
615     break;
616 
617   case 'G':            /* grid spec; just warn that we don't pay attention to spacing */
618     warn(W_GRID);
619     break;
620 
621   case 'L':            /* language spec; no further work required */
622     break;
623 
624   case 'l':
625     if (sscanf(optarg, "%lf", &tmp_dbl) == 1 && 0 <= tmp_dbl && tmp_dbl <= 2.0)
626       Line_weight = tmp_dbl;
627     else
628       fprintf(stderr, "warning: bad line weight %s, expected 0 to 2.0\n", optarg);
629     break;
630 
631   case 'n':
632     if (sscanf(optarg, "%d", &tmp_int) == 1 && 0 <= tmp_int && tmp_int <= 3)
633       Page_mode = tmp_int;
634     else
635       fprintf(stderr, "warning: bad page mode (0, 1, 2, or 3 expected)\n");
636     break;
637 
638   case 'P':
639     Page_mode = PM_SCALED_PAGE;
640     break;
641 
642   case 'p':
643     Pic_convert_p = 1;
644     if (strcmp(optarg, "-") != 0) {
645       /* copy while converting slashes */
646       for (i = 0; optarg[i] && i < ARRAY_SIZE(Pic_convert_dir) - 2; i++)
647         Pic_convert_dir[i] = (optarg[i] == '\\') ? '/' : optarg[i];
648 
649       /* append slash if not there already */
650       if (i > 0 && Pic_convert_dir[i - 1] != '/')
651         Pic_convert_dir[i++] = '/';
652       Pic_convert_dir[i] = '\0';
653     }
654     sprintf(fn, "%sreadme.txt", Pic_convert_dir);
655     f = fopen(fn, "w");
656     if (!f) {
657       fprintf(stderr, "can't write the eps conversion directory %s.\n", Pic_convert_dir);
658       exit(1);
659     }
660     fprintf(f,
661 	    "This directory has been used by the fig2dev pstricks driver.\n"
662 	    "Any '.eps' file here may be overwritten.\n");
663     fclose(f);
664     break;
665 
666   case 'R':
667     if (sscanf(optarg, "%d", &tmp_int) == 1 && 0 <= tmp_int && tmp_int <= 2)
668       Arrows = tmp_int;
669     else
670       fprintf(stderr, "warning: bad arrow spec (0, 1, or 2 expected)\n");
671     break;
672 
673   case 'S':
674     if (optarg && sscanf(optarg, "%d", &tmp_int) == 1 && (tmp_int < 8 || tmp_int > 12)) {
675       fprintf(stderr, "Scale must be between 8 and 12 inclusively\n");
676       exit(1);
677     }
678     mag = ScaleTbl[tmp_int - 8].mag;
679     font_size = (double) ScaleTbl[tmp_int - 8].size;
680     break;
681 
682   case 't':
683     if (parse_pst_version(tmp_pst_version, optarg) == 0) {
684       pst_version_cpy(Pst_version, tmp_pst_version);
685       /* compare user's version with first version that had linejoin to see
686 	 which style of linejoin command to emit */
687       parse_pst_version(tmp_pst_version, STRINGIFY(PST_LINEJOIN_VERSION));
688       Linejoin = (pst_version_cmp(Pst_version, tmp_pst_version) < 0) ? LJ_PSTVERB : LJ_PSTOPTION;
689     }
690     else
691       fprintf(stderr, "warning: bad PSTricks version '%s' was ignored\n", optarg);
692     break;
693 
694   case 'v':
695     Verbose = True;    /* put extra info in output LaTex file */
696     break;
697 
698   case 'x':
699     if (sscanf(optarg, "%lf", &tmp_dbl) == 1 && 0 <= tmp_dbl && tmp_dbl <= 100.0)
700       X_margin = 1200/2.54 * tmp_dbl;
701     else
702       fprintf(stderr, "warning: bad x margin setting ignored\n");
703     break;
704 
705   case 'y':
706     if (sscanf(optarg, "%lf", &tmp_dbl) == 1 && 0 <= tmp_dbl && tmp_dbl <= 100.0)
707       Y_margin = 1200/2.54 * tmp_dbl;
708     else
709       fprintf(stderr, "warning: bad y margin setting ignored\n");
710     break;
711 
712   case 'z':
713     if (sscanf(optarg, "%d", &tmp_int) == 1 && 0 <= tmp_int && tmp_int <= 2)
714       Font_handling = tmp_int;
715     else
716       fprintf(stderr, "warning: bad font spec (0, 1, or 2 expected)\n");
717     break;
718 
719   default:
720     put_msg(Err_badarg, opt, "pstricks");
721     exit(1);
722   }
723 }
724 
725 /**********************************************************************/
726 /* start/end                                                          */
727 /**********************************************************************/
728 
729 /* possible extra packages needed */
730 #define EP_GRAPHICX     0
731 #define EP_PSTRICKS_ADD 1
732 
733 static char *extra_package_names[] = {
734   "graphicx",
735   "pstricks-add",
736 };
737 
738 static struct {
739   unsigned extra_package_mask;
740   int has_text_p;
741 } Preprocessed_data[1];
742 
743 static void
pp_check_arrow_style(arrow)744 pp_check_arrow_style(arrow)
745      F_arrow *arrow;
746 {
747   if (!arrow)
748     return;
749   if (arrow->style == 0) /* hollow arrow */
750     Preprocessed_data->extra_package_mask |= bit(EP_PSTRICKS_ADD);
751 }
752 
753 static void
pp_check_line_style(style)754 pp_check_line_style(style)
755      int style;
756 {
757   if (style == DASH_2_DOTS_LINE || style == DASH_3_DOTS_LINE)
758     Preprocessed_data->extra_package_mask |= bit(EP_PSTRICKS_ADD);
759 }
760 
761 /* This is just so we can emit a minimal
762    \usepackage{} in page mode.
763    Much work for a small result! */
764 static void
pp_find_pstricks_extras(objects)765 pp_find_pstricks_extras(objects)
766      F_compound *objects;
767 {
768   F_line *line;
769   F_ellipse *ellipse;
770   F_spline *spline;
771   F_arc *arc;
772 
773   if (!objects)
774     return;
775 
776   for (line = objects->lines; line; line = line->next) {
777     pp_check_line_style(line->style);
778     pp_check_arrow_style(line->for_arrow);
779     pp_check_arrow_style(line->back_arrow);
780     if (line->type == T_PIC_BOX)
781       Preprocessed_data->extra_package_mask |= bit(EP_GRAPHICX);
782   }
783 
784   for (ellipse = objects->ellipses; ellipse; ellipse = ellipse->next)
785     pp_check_line_style(ellipse->style);
786 
787   for (spline = objects->splines; spline; spline = spline->next) {
788     pp_check_line_style(spline->style);
789     pp_check_arrow_style(spline->for_arrow);
790     pp_check_arrow_style(spline->back_arrow);
791   }
792 
793   for (arc = objects->arcs; arc; arc = arc->next) {
794     pp_check_line_style(arc->style);
795     pp_check_arrow_style(arc->for_arrow);
796     pp_check_arrow_style(arc->back_arrow);
797   }
798 
799   pp_find_pstricks_extras(objects->compounds);
800   pp_find_pstricks_extras(objects->next);
801 }
802 
803 void
pp_find_text(objects)804 pp_find_text(objects)
805      F_compound *objects;
806 {
807   if (!objects)
808     return;
809 
810   if (objects->texts)
811     Preprocessed_data->has_text_p = 1;
812   if (!Preprocessed_data->has_text_p)
813     pp_find_text(objects->compounds);
814   if (!Preprocessed_data->has_text_p)
815     pp_find_text(objects->next);
816 }
817 
818 /* Make preprocessing passes over the object tree. */
819 static void
preprocess(objects)820 preprocess(objects)
821      F_compound *objects;
822 {
823   pp_find_pstricks_extras(objects);
824   pp_find_text(objects);
825 }
826 
827 static void
define_setfont(tfp)828 define_setfont(tfp)
829      FILE *tfp;
830 {
831   if (!Preprocessed_data->has_text_p)
832     return;
833 
834 #ifdef NFSS
835   if (Font_handling == FH_SIZE_ONLY)
836     fprintf(tfp,
837 	    "%%\n"
838 	    "\\begingroup\\makeatletter%%\n"
839 	    "\\ifx\\SetFigFontSizeOnly\\undefined%%\n"
840 	    "  \\gdef\\SetFigFont#1#2{%%\n"
841 	    "    \\fontsize{#1}{#2pt}%%\n"
842 	    "    \\selectfont}%%\n"
843 	    "\\fi\n"
844 	    "\\endgroup%%\n");
845   else if (Font_handling == FH_FULL)
846     fprintf(tfp,
847 	    "%%\n"
848 	    "\\begingroup\\makeatletter%%\n"
849 	    "\\ifx\\SetFigFont\\undefined%%\n"
850 	    "  \\gdef\\SetFigFont#1#2#3#4#5{%%\n"
851 	    "    \\reset@font\\fontsize{#1}{#2pt}%%\n"
852 	    "    \\fontfamily{#3}\\fontseries{#4}\\fontshape{#5}%%\n"
853 	    "    \\selectfont}%%\n"
854 	    "\\fi\n"
855 	    "\\endgroup%%\n");
856 #else
857   if (Font_handling != FH_NONE)
858     fprintf(tfp,
859 	    "%%\n"
860 	    "\\begingroup\\makeatletter%%\n"
861 	    "\\ifx\\SetFigFont\\undefined%%\n"
862 	    "%%   extract first six characters in \\fmtname\n"
863 	    "\\def\\x#1#2#3#4#5#6#7\\relax{\\def\\x{#1#2#3#4#5#6}}%%\n"
864 	    "\\expandafter\\x\\fmtname xxxxxx\\relax \\def\\y{splain}%%\n"
865 	    "\\ifx\\x\\y   %% LaTeX or SliTeX?\n"
866 	    "\\gdef\\SetFigFont#1#2#3{%%\n"
867 	    "  \\ifnum #1<17\\tiny\\else \\ifnum #1<20\\small\\else\n"
868 	    "  \\ifnum #1<24\\normalsize\\else \\ifnum #1<29\\large\\else\n"
869 	    "  \\ifnum #1<34\\Large\\else \\ifnum #1<41\\LARGE\\else\n"
870 	    "     \\huge\\fi\\fi\\fi\\fi\\fi\\fi\n"
871 	    "  \\csname #3\\endcsname}%%\n"
872 	    "\\else\n"
873 	    "\\gdef\\SetFigFont#1#2#3{\\begingroup\n"
874 	    "  \\count@#1\\relax \\ifnum 25<\\count@\\count@25\\fi\n"
875 	    "  \\def\\x{\\endgroup\\@setsize\\SetFigFont{#2pt}}%%\n"
876 	    "  \\expandafter\\x\n"
877 	    "    \\csname \\romannumeral\\the\\count@ pt\\expandafter\\endcsname\n"
878 	    "    \\csname @\\romannumeral\\the\\count@ pt\\endcsname\n"
879 	    "  \\csname #3\\endcsname}%%\n"
880 	    "\\fi\n"
881 	    "\\fi\\endgroup\n");
882 #endif
883 }
884 
885 void
genpstrx_start(objects)886 genpstrx_start(objects)
887      F_compound *objects;
888 {
889   int i;
890   double unit, pllx, plly, purx, pury;
891 
892   /* Run the preprocessor. */
893   preprocess(objects);
894 
895   /* If user gave font size, set up tables */
896   texfontsizes[0] = texfontsizes[1] =
897     TEXFONTSIZE(font_size != 0.0 ? font_size : DEFAULT_FONT_SIZE);
898   define_setfont(tfp);
899 
900   /* print header information */
901   if (Verbose) {
902     fprintf(tfp,
903       "%% PSTricks driver for Fig Version "
904       STRINGIFY(PST_DRIVER_VERSION)
905       " by Gene Ressler.\n");
906     fprintf(tfp,
907       "%% PSTricks version %s is assumed in Sketch output (change with -t).\n",
908       Pst_version->str);
909   }
910 
911   /* print any whole-figure comments prefixed with "%" */
912   if (objects->comments) {
913     fprintf(tfp,"%%\n");
914     print_comments("% ",objects->comments,"");
915     fprintf(tfp,"%%\n");
916   }
917 
918   /* print preamble if user asked for complete tex file */
919   if (Page_mode >= PM_PAGE) {
920     fprintf(tfp,
921 	    "\\documentclass{article}\n"
922 	    "\\pagestyle{empty}\n"
923 	    "\\setlength\\topmargin{-.5in}\n"
924 	    "\\setlength\\textheight{10in}\n"
925 	    "\\setlength\\oddsidemargin{-.5in}\n"
926 	    "\\setlength\\evensidemargin\\oddsidemargin\n"
927 	    "\\setlength\\textwidth{7.5in}\n"
928 	    "\\usepackage{pstricks");
929     /* mask was set up by the preprocessor */
930     for (i = 0; i < 31; i++) {
931       if (Preprocessed_data->extra_package_mask & bit(i))
932         fprintf(tfp, ",%s", extra_package_names[i]);
933     }
934     fprintf(tfp,
935 	    "}\n"
936 	    "\\begin{document}\n"
937 	    "\\begin{center}\n");
938   }
939 
940   pllx = cm_from_1200ths(-X_margin);
941   plly = cm_from_1200ths(-Y_margin);
942   purx = cm_from_1200ths(1 + abs_int(urx - llx) + X_margin);
943   pury = cm_from_1200ths(1 + abs_int(ury - lly) + Y_margin);
944 
945   /* default unit is 1cm */
946   unit = 1.0;
947   if (Page_mode == PM_SCALED_PAGE) {
948     /* allow a small epsilon for page size to avoid spurious blank page */
949 #define PAGE_EPS .01
950     unit = min_dbl(1.0, (7.5 * 2.54 - PAGE_EPS) / (purx - pllx));
951     unit = min_dbl(unit, (10 * 2.54 - PAGE_EPS) / (pury - plly));
952     if (unit < 1.0) {
953       fprintf(tfp, "\\psset{unit="DBL"}\n", unit);
954       warn(W_PAGESCALE);
955     }
956     /* this works only roughly */
957     fontmag *= unit;
958   }
959   /* Set up unit length for pictures if there are any.  We can
960      get that from the preprocessor pass flags. */
961   if (Preprocessed_data->extra_package_mask & bit(EP_GRAPHICX)) {
962     fprintf(tfp,
963 	    "%% Following sets up unit length for picture dimensions. If you \n"
964 	    "%% do \\psset{unit=LEN} to scale the picture, you must also\n"
965 	    "%% \\newlength\\unit\\setlength\\unit{LEN} for the bitmaps to match.\n"
966 	    "\\makeatletter%%\n"
967 	    "\\@ifundefined{unit}{\\newlength\\unit\\setlength\\unit{%.3lfcm}}%%\n"
968 	    "\\makeatother%%\n",
969 	    unit);
970     warn(W_UNIT);
971   }
972   if (Page_mode != PM_BARE) {
973     fprintf(tfp,
974 	    "\\begin{pspicture}("DBL","DBL")("DBL","DBL")\n",
975 	    pllx, plly, purx, pury);
976   }
977 }
978 
979 int
genpstrx_end()980 genpstrx_end()
981 {
982   if (Page_mode != PM_BARE)
983     fprintf(tfp, "\\end{pspicture}\n");
984 
985   if (Page_mode >= PM_PAGE) {
986     fprintf(tfp,
987 	    "\\end{center}\n"
988 	    "\\end{document}\n");
989   }
990   issue_warnings(stderr);
991 
992   /* return is determined by warning mechanism above */
993   return rtn_val;
994 }
995 
996 /**********************************************************************/
997 /* general purpose drawing                                            */
998 /**********************************************************************/
999 
1000 /* color table --
1001    1 in the "defined" field means this color is already declared by pstricks;
1002    shades and tints are boolean bit fields that denote whether a declaration
1003    for the corresponding-numbered shade or tint has already been emitted
1004    (initially all _not_) bits 1-19 are shades 1-19 and tints 21-39 respectively */
1005 static
1006 struct color_table_entry_t {
1007   char name[24];
1008   Boolean defined_p;
1009   double r, g, b;
1010   unsigned shades, tints;
1011 } color_table[544] = {
1012   {"black",     1, 0.00, 0.00, 0.00, 0u, 0u},	/* black */
1013   {"blue",      1, 0.00, 0.00, 1.00, 0u, 0u},	/* blue */
1014   {"green",     1, 0.00, 1.00, 0.00, 0u, 0u},	/* green */
1015   {"cyan",      1, 0.00, 1.00, 1.00, 0u, 0u},	/* cyan */
1016   {"red",       1, 1.00, 0.00, 0.00, 0u, 0u},	/* red */
1017   {"magenta",   1, 1.00, 0.00, 1.00, 0u, 0u},	/* magenta */
1018   {"yellow",    1, 1.00, 1.00, 0.00, 0u, 0u},	/* yellow */
1019   {"white",     1, 1.00, 1.00, 1.00, 0u, 0u},	/* white */
1020   {"bluei",     0, 0.00, 0.00, 0.56, 0u, 0u},	/* blue1 */
1021   {"blueii",    0, 0.00, 0.00, 0.69, 0u, 0u},	/* blue2 */
1022   {"blueiii",   0, 0.00, 0.00, 0.82, 0u, 0u},	/* blue3 */
1023   {"blueiv",    0, 0.53, 0.81, 1.00, 0u, 0u},	/* blue4 */
1024   {"greeni",    0, 0.00, 0.56, 0.00, 0u, 0u},	/* green1 */
1025   {"greenii",   0, 0.00, 0.69, 0.00, 0u, 0u},	/* green2 */
1026   {"greeniii",  0, 0.00, 0.82, 0.00, 0u, 0u},	/* green3 */
1027   {"cyani",     0, 0.00, 0.56, 0.56, 0u, 0u},	/* cyan1 */
1028   {"cyanii",    0, 0.00, 0.69, 0.69, 0u, 0u},	/* cyan2 */
1029   {"cyaniii",   0, 0.00, 0.82, 0.82, 0u, 0u},	/* cyan3 */
1030   {"redi",      0, 0.56, 0.00, 0.00, 0u, 0u},	/* red1 */
1031   {"redii",     0, 0.69, 0.00, 0.00, 0u, 0u},	/* red2 */
1032   {"rediii",    0, 0.82, 0.00, 0.00, 0u, 0u},	/* red3 */
1033   {"magentai",  0, 0.56, 0.00, 0.56, 0u, 0u},	/* magenta1 */
1034   {"magentaii", 0, 0.69, 0.00, 0.69, 0u, 0u},	/* magenta2 */
1035   {"magentaiii",0, 0.82, 0.00, 0.82, 0u, 0u},	/* magenta3 */
1036   {"browni",    0, 0.50, 0.19, 0.00, 0u, 0u},	/* brown1 */
1037   {"brownii",   0, 0.63, 0.25, 0.00, 0u, 0u},	/* brown2 */
1038   {"browniii",  0, 0.75, 0.38, 0.00, 0u, 0u},	/* brown3 */
1039   {"pinki",     0, 1.00, 0.50, 0.50, 0u, 0u},	/* pink1 */
1040   {"pinkii",    0, 1.00, 0.63, 0.63, 0u, 0u},	/* pink2 */
1041   {"pinkiii",   0, 1.00, 0.75, 0.75, 0u, 0u},	/* pink3 */
1042   {"pinkiv",    0, 1.00, 0.88, 0.88, 0u, 0u},	/* pink4 */
1043   {"gold",      0, 1.00, 0.84, 0.00, 0u, 0u}	/* gold */
1044 };
1045 
1046 /* certain indices we need */
1047 #define CT_BLACK  0
1048 #define CT_WHITE  7
1049 
1050 static void
setup_color_table()1051 setup_color_table()
1052 {
1053   int i;
1054   char rn[100];
1055   static int done_p = 0;
1056 
1057   if (done_p) return;
1058 
1059   for (i = 0; i < num_usr_cols; i++) {
1060     int iuc = i + NUM_STD_COLS;
1061     sprintf(color_table[iuc].name, "usrclr%s", roman_numeral_from_int(rn, i));
1062     color_table[iuc].r = user_colors[i].r / 256.0;
1063     color_table[iuc].g = user_colors[i].g / 256.0;
1064     color_table[iuc].b = user_colors[i].b / 256.0;
1065   }
1066   done_p = 1;
1067 }
1068 
1069 /* die if a bad color index is seen */
1070 static void
check_color_index(ic)1071 check_color_index(ic)
1072      int ic;
1073 {
1074   if (ic < 0 || ic >= NUM_STD_COLS + num_usr_cols) {
1075     fprintf(stderr, "bad color index (%d; max=%d)\n", ic, NUM_STD_COLS - 1 + num_usr_cols);
1076     exit(1);
1077   }
1078 }
1079 
1080 /* interpolate two color values given indices for them in the table
1081    and the interpolation parameter; used for tints and shades */
1082 static void
interpolate_colors(r,g,b,ic0,ic1,t)1083 interpolate_colors(r, g, b, ic0, ic1, t)
1084      double *r, *g, *b;
1085      int ic0, ic1;
1086      double t;
1087 {
1088   if (t < 0 || t > 1) {
1089     fprintf(stderr, "bad color interpolation parameter ("DBL")\n", t);
1090     exit(1);
1091   }
1092   check_color_index(ic0);
1093   check_color_index(ic1);
1094   setup_color_table();
1095 
1096   *r = color_table[ic0].r + t * (color_table[ic1].r - color_table[ic0].r);
1097   *g = color_table[ic0].g + t * (color_table[ic1].g - color_table[ic0].g);
1098   *b = color_table[ic0].b + t * (color_table[ic1].b - color_table[ic0].b);
1099 }
1100 
1101 /* return the name of a color given its index; may
1102    emit a color declaration if it's needed */
1103 static char *
color_name_after_declare_color(int ic)1104 color_name_after_declare_color(int ic)
1105 {
1106   setup_color_table();
1107   check_color_index(ic);
1108   /* check if color declaration is needed */
1109   if (!color_table[ic].defined_p) {
1110     if (Verbose)
1111       fprintf(tfp, "%% declare color %d\n", ic);
1112     fprintf(tfp, "\\newrgbcolor{%s}{"DBL" "DBL" "DBL"}%%\n",
1113 	    color_table[ic].name,
1114 	    color_table[ic].r,
1115 	    color_table[ic].g,
1116 	    color_table[ic].b);
1117     color_table[ic].defined_p = 1;
1118   }
1119   return color_table[ic].name;
1120 }
1121 
1122 /* return the name of a tint or shade given its index;
1123    may emit a color declaration if it's needed; we make
1124    the user specify a string buffer for the name because
1125    a static table would be huge */
1126 static char *
shade_or_tint_name_after_declare_color(name,ist,ic)1127 shade_or_tint_name_after_declare_color(name, ist, ic)
1128      char *name;
1129      int ist, ic;
1130 {
1131   char *special, rn[100];
1132   double r, g, b;
1133 
1134   if (ist < 0 || ist > 40) {
1135     fprintf(stderr, "bad shade/tint index (%d)\n", ist);
1136     exit(1);
1137   }
1138 
1139   /* black, white, and full saturation are special cases */
1140   if (ist == 0)
1141     special = color_name_after_declare_color(CT_BLACK);
1142   else if (ist == 20)
1143     special = color_name_after_declare_color(ic);
1144   else if (ist == 40)
1145     special = color_name_after_declare_color(CT_WHITE);
1146   else
1147     special = 0;
1148   if (special) {
1149     strcpy(name, special);
1150     return name;
1151   }
1152 
1153   /* make sure name and rgb value of full sat color are available in table */
1154   setup_color_table();
1155 
1156   /* name is color with shade/tint index appended */
1157   sprintf(name, "%sst%s", color_table[ic].name, roman_numeral_from_int(rn, ist));
1158   if (ist < 20 && (color_table[ic].shades & bit(ist)) == 0) {
1159     /* this is a shade needing a declaration */
1160     if (Verbose)
1161       fprintf(tfp, "%% declare shade %d of %s\n", ist, color_table[ic].name);
1162     interpolate_colors(&r, &g, &b, CT_BLACK, ic, (double)ist/20.0);
1163     fprintf(tfp, "\\newrgbcolor{%s}{"DBL" "DBL" "DBL"}%%\n", name, r, g, b);
1164     color_table[ic].shades |= bit(ist);
1165   }
1166   else if (ist > 20 && (color_table[ic].tints & bit(ist - 20)) == 0) {
1167     /* this is a tint needing a declaration */
1168     if (Verbose)
1169       fprintf(tfp, "%% declare tint %d of %s\n", ist, color_table[ic].name);
1170     interpolate_colors(&r, &g, &b, ic, CT_WHITE, (double)(ist - 20)/20.0);
1171     fprintf(tfp, "\\newrgbcolor{%s}{"DBL" "DBL" "DBL"}%%\n", name, r, g, b);
1172     color_table[ic].tints |= bit(ist - 20);
1173   }
1174   return name;
1175 }
1176 
1177 /* return the name of a gray value given its intensity as a
1178    the numerator over 20 - 0 is black and 20 is white; may
1179    emit a gray declaration if it's needed */
1180 static char *
gray_name_after_declare_gray(int ig)1181 gray_name_after_declare_gray(int ig)
1182 {
1183   static char names[20][16]; /* grayxviii */
1184   char rn[100];
1185 
1186   if (ig < 0 || ig > 20) {
1187     fprintf(stderr, "bad gray value (%d)\n", ig);
1188     exit(1);
1189   }
1190   /* black and white are special cases */
1191   if (ig == 0)
1192     return color_name_after_declare_color(CT_BLACK);
1193   if (ig == 20)
1194     return color_name_after_declare_color(CT_WHITE);
1195 
1196   /* check if gray level declaration is needed */
1197   if (names[ig][0] == '\0') {
1198     if (Verbose)
1199       fprintf(tfp, "%% declare gray %d\n", ig);
1200     sprintf(names[ig], "gray%s", roman_numeral_from_int(rn, ig));
1201     fprintf(tfp, "\\newgray{%s}{"DBL"}%%\n", names[ig], (double)ig/20.0);
1202   }
1203   return names[ig];
1204 }
1205 
1206 /* print points, 4 per line */
1207 #define PP_NORMAL    0
1208 #define PP_SKIP_LAST 1
1209 #define PP_REPEAT_2  2
1210 
1211 static void
put_points(list,flag)1212 put_points(list, flag)
1213      F_point *list;
1214      int flag;
1215 {
1216   F_point *p;
1217   int n_points = 0;
1218   int countdown = -1;
1219 
1220   for (p = list;;) {
1221     fprintf(tfp, "("DBL","DBL")",
1222 	    cm_from_1200ths(p->x),
1223 	    cm_from_1200ths(p->y));
1224     ++n_points;
1225     --countdown;
1226     p = p->next;
1227     if (!p && flag == PP_REPEAT_2 && n_points > 2) {
1228       p = list;
1229       countdown = 2;
1230     }
1231     if (!p || (flag == PP_SKIP_LAST && !p->next) || countdown == 0) {
1232       fprintf(tfp, "\n");
1233       return;
1234     }
1235     if (n_points % 4 == 0)
1236       fprintf(tfp, "\n\t");
1237   }
1238 }
1239 
1240 /* print line/polygon commands to form arrows as Fig does;
1241    the alternative is to use pstricks arrow end formatting,
1242    which is done in format_options() */
1243 #define STICK_ARROW                     0
1244 #define TRIANGLE_ARROW                  1
1245 #define INDENTED_BUTT_ARROW             2
1246 #define POINTED_BUTT_ARROW              3
1247 #define LAST_OLD_ARROW POINTED_BUTT_ARROW
1248 /* These are new arrow types as of xfig 3.2.5a */
1249 #define DIAMOND_ARROW                   4
1250 #define CIRCLE_ARROW                    5
1251 #define SEMI_CIRCLE_ARROW               6
1252 #define RECTANGLE_ARROW                 7
1253 #define REVERSE_TRIANGLE_ARROW          8
1254 #define BISHADE_INDENTED_BUTT_ARROW     9
1255 #define TRIANGLE_HALF_ARROW            10
1256 #define INDENTED_BUTT_HALF_ARROW       11
1257 #define POINTED_BUTT_HALF_ARROW        12
1258 #define PARTIAL_REVERSE_TRIANGLE_ARROW 13
1259 #define PARTIAL_RECTANGLE_ARROW        14
1260 
1261 #define HOLLOW_ARROW 0
1262 #define FILLED_ARROW 1
1263 
1264 static void
put_arrowhead(x_hd,y_hd,x_dir,y_dir,wid,len,thickness,type,style,pen_color)1265 put_arrowhead(x_hd, y_hd,
1266               x_dir, y_dir,
1267               wid, len, thickness,
1268               type, style,
1269               pen_color)
1270      double x_hd, y_hd, x_dir, y_dir, wid, len, thickness;
1271      int type, style, pen_color;
1272 {
1273   double dir_len,
1274     x_bas, y_bas, /* base point of the arrow */
1275     x_prp, y_prp, /* unit perp vector wrt arrow direction */
1276     x_lft, y_lft, /* left base point */
1277     x_rgt, y_rgt, /* right base point */
1278     x_lfa, y_lfa, /* left alternate point */
1279     x_rga, y_rga, /* right alternate point */
1280     t_lft, t_rgt; /* arc (semicircle) arrowhead thetas */
1281   char fill_fillcolor_option[50], linecolor_option[50];
1282   char *pen_color_name, *fillcolor_option, *blank_fillcolor_option;
1283 
1284   /* set up the pstricks options to fill and leave blank the arrow center */
1285   blank_fillcolor_option = ",fillcolor=white";
1286   if (pen_color == -1) {
1287     fill_fillcolor_option[0] = '\0';
1288     linecolor_option[0] = '\0';
1289   }
1290   else {
1291     pen_color_name = color_name_after_declare_color(pen_color);
1292     sprintf(fill_fillcolor_option, ",fillcolor=%s", pen_color_name);
1293     sprintf(linecolor_option, ",linecolor=%s", pen_color_name);
1294   }
1295 
1296   /* assign arrow fill options based on style flag */
1297   fillcolor_option = (style == HOLLOW_ARROW) ? blank_fillcolor_option : fill_fillcolor_option;
1298 
1299   /* convert to centimeters */
1300   x_hd = cm_from_1200ths(x_hd);
1301   y_hd = cm_from_1200ths(y_hd);
1302   wid = cm_from_1200ths(wid);
1303   len = cm_from_1200ths(len);
1304   thickness = cm_from_1200ths(thickness) * Line_weight;
1305 
1306   /* make direction vector unit length */
1307   dir_len = sqrt(x_dir * x_dir + y_dir * y_dir);
1308   x_dir /= dir_len;
1309   y_dir /= dir_len;
1310 
1311   /* find base of arrowhead */
1312   x_bas = x_hd - x_dir * len;
1313   y_bas = y_hd - y_dir * len;
1314 
1315   /* find unit perp from direction */
1316   x_prp = -y_dir;
1317   y_prp = x_dir;
1318 
1319   /* normal baseline endpoints */
1320   x_lft = x_bas + x_prp * 0.5 * wid;
1321   y_lft = y_bas + y_prp * 0.5 * wid;
1322 
1323   x_rgt = x_lft - x_prp * wid;
1324   y_rgt = y_lft - y_prp * wid;
1325 
1326   /* alternate baseline endpoints */
1327   x_lfa = x_hd + x_prp * 0.5 * wid;
1328   y_lfa = y_hd + y_prp * 0.5 * wid;
1329 
1330   x_rga = x_lfa - x_prp * wid;
1331   y_rga = y_lfa - y_prp * wid;
1332 
1333   /* warn user if a new arrow type is being used */
1334   if (type > LAST_OLD_ARROW)
1335     warn(W_NEW_ARROW);
1336 
1337   /* draw based on type */
1338   switch (type) {
1339   case STICK_ARROW:
1340   simple_arrow:
1341     if (Verbose)
1342       fprintf(tfp, "%% stick arrow\n");
1343     fprintf(tfp,
1344 	    "\\psline[linewidth="DBL"%s]{c-c}"
1345 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1346 	    thickness, linecolor_option,
1347 	    x_lft, y_lft, x_hd, y_hd, x_rgt, y_rgt);
1348     break;
1349 
1350   case TRIANGLE_ARROW:
1351     if (Verbose)
1352       fprintf(tfp, "%% triangle arrow\n");
1353     fprintf(tfp,
1354 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1355 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1356 	    thickness, linecolor_option, fillcolor_option,
1357 	    x_hd, y_hd, x_lft, y_lft, x_rgt, y_rgt);
1358     break;
1359 
1360   case INDENTED_BUTT_ARROW:
1361     x_bas += x_dir * len * 0.3;
1362     y_bas += y_dir * len * 0.3;
1363     if (Verbose)
1364       fprintf(tfp, "%% indented butt arrow\n");
1365     fprintf(tfp,
1366 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1367 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1368 	    thickness, linecolor_option, fillcolor_option,
1369 	    x_hd, y_hd, x_lft, y_lft, x_bas, y_bas, x_rgt, y_rgt);
1370     break;
1371 
1372   case POINTED_BUTT_ARROW:
1373     /*
1374      * old method was consistent with fig2dev calc_arrow; we're
1375      * changing to match Andreas
1376      * x_bas -= x_dir * len * 0.3;
1377      * y_bas -= y_dir * len * 0.3;
1378     */
1379     x_lft += x_dir * len * 0.3;
1380     y_lft += y_dir * len * 0.3;
1381     x_rgt += x_dir * len * 0.3;
1382     y_rgt += y_dir * len * 0.3;
1383     if (Verbose)
1384       fprintf(tfp, "%% pointed butt arrow\n");
1385     fprintf(tfp,
1386 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1387 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1388 	    thickness, linecolor_option, fillcolor_option,
1389 	    x_hd, y_hd, x_lft, y_lft, x_bas, y_bas, x_rgt, y_rgt);
1390     break;
1391 
1392   case DIAMOND_ARROW:
1393     x_lft += x_dir * len * 0.5;
1394     y_lft += y_dir * len * 0.5;
1395     x_rgt += x_dir * len * 0.5;
1396     y_rgt += y_dir * len * 0.5;
1397     if (Verbose)
1398       fprintf(tfp, "%% diamond arrow\n");
1399     fprintf(tfp,
1400 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1401 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1402 	    thickness, linecolor_option, fillcolor_option,
1403 	    x_hd, y_hd, x_lft, y_lft, x_bas, y_bas, x_rgt, y_rgt);
1404    break;
1405 
1406   case CIRCLE_ARROW:
1407     if (Verbose)
1408       fprintf(tfp, "%% circle arrow\n");
1409     fprintf(tfp,
1410 	    "\\pscircle[linewidth="DBL"%s,fillstyle=solid%s]("DBL","DBL"){"DBL"}%%\n",
1411 	    thickness, linecolor_option, fillcolor_option,
1412 	    x_hd - x_dir * len/2, y_hd - y_dir * len/2, len/2);
1413     break;
1414 
1415   case SEMI_CIRCLE_ARROW:
1416     t_lft = atan2(y_prp, x_prp) * (180.0 / PI);
1417     t_rgt = t_lft + 180;
1418     if (Verbose)
1419       fprintf(tfp, "%% semicircle arrow\n");
1420     fprintf(tfp,
1421 	    "\\psarc[linewidth="DBL"%s,fillstyle=solid%s]"
1422 	    "("DBL","DBL"){"DBL"}{"DBL"}{"DBL"}%%\n",
1423 	    thickness, linecolor_option, fillcolor_option,
1424 	    x_hd, y_hd, len/2, t_lft, t_rgt);
1425    break;
1426 
1427   case RECTANGLE_ARROW:
1428     if (Verbose)
1429       fprintf(tfp, "%% rectangle arrow\n");
1430     fprintf(tfp,
1431 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1432 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1433 	    thickness, linecolor_option, fillcolor_option,
1434 	    x_lfa, y_lfa, x_lft, y_lft, x_rgt, y_rgt, x_rga, y_rga);
1435     break;
1436 
1437   case REVERSE_TRIANGLE_ARROW:
1438     if (Verbose)
1439       fprintf(tfp, "%% reverse triangle arrow\n");
1440     fprintf(tfp,
1441 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1442 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1443 	    thickness, linecolor_option, fillcolor_option,
1444 	    x_bas, y_bas, x_rga, y_rga, x_lfa, y_lfa);
1445     break;
1446 
1447   case BISHADE_INDENTED_BUTT_ARROW:
1448     /* maybe could do this in 2 commands with a clip region */
1449     x_bas += x_dir * len * 0.3;
1450     y_bas += y_dir * len * 0.3;
1451     if (Verbose)
1452       fprintf(tfp, "%% bishade indented butt arrow\n");
1453     /* clear the arrowhead area */
1454     fprintf(tfp,
1455 	    "\\pspolygon[linestyle=none,fillstyle=solid%s]"
1456 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1457 	    blank_fillcolor_option,
1458 	    x_hd, y_hd, x_lft, y_lft, x_bas, y_bas, x_rgt, y_rgt);
1459     /* fill the half that's filled */
1460     if (style == HOLLOW_ARROW) {
1461       fprintf(tfp,
1462 	      "\\pspolygon[linestyle=none,fillstyle=solid%s]"
1463 	      "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1464 	      fill_fillcolor_option,
1465 	      x_hd, y_hd, x_lft, y_lft, x_bas, y_bas);
1466     }
1467     else {
1468       fprintf(tfp,
1469 	      "\\pspolygon[linestyle=none,fillstyle=solid%s]"
1470 	      "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1471 	      fill_fillcolor_option,
1472 	      x_bas, y_bas, x_rgt, y_rgt, x_hd, y_hd);
1473     }
1474     /* rule the outline */
1475     fprintf(tfp,
1476 	    "\\pspolygon[linewidth="DBL"%s]"
1477 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1478 	    thickness, linecolor_option,
1479 	    x_hd, y_hd, x_lft, y_lft, x_bas, y_bas, x_rgt, y_rgt);
1480     break;
1481 
1482   case TRIANGLE_HALF_ARROW:
1483      if (Verbose)
1484       fprintf(tfp, "%% triangle half-arrow\n");
1485     fprintf(tfp,
1486 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1487 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1488 	    thickness, linecolor_option, fillcolor_option,
1489 	    x_bas, y_bas, x_rgt, y_rgt, x_hd, y_hd);
1490    break;
1491 
1492   case INDENTED_BUTT_HALF_ARROW:
1493     x_bas += x_dir * len * 0.3;
1494     y_bas += y_dir * len * 0.3;
1495     if (Verbose)
1496       fprintf(tfp, "%% indented butt half-arrow\n");
1497     fprintf(tfp,
1498 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1499 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1500 	    thickness, linecolor_option, fillcolor_option,
1501 	    x_bas, y_bas, x_rgt, y_rgt, x_hd, y_hd);
1502     break;
1503 
1504   case POINTED_BUTT_HALF_ARROW:
1505     x_rgt += x_dir * len * 0.3;
1506     y_rgt += y_dir * len * 0.3;
1507     if (Verbose)
1508       fprintf(tfp, "%% pointed butt half-arrow\n");
1509     fprintf(tfp,
1510 	    "\\pspolygon[linewidth="DBL"%s,fillstyle=solid%s]"
1511 	    "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1512 	    thickness, linecolor_option, fillcolor_option,
1513 	    x_hd, y_hd, x_bas, y_bas, x_rgt, y_rgt);
1514     break;
1515 
1516   case PARTIAL_REVERSE_TRIANGLE_ARROW:
1517     if (style == HOLLOW_ARROW) {
1518       if (Verbose)
1519 	fprintf(tfp, "%% reverse stick arrow\n");
1520       fprintf(tfp,
1521 	      "\\psline[linewidth="DBL"%s,fillstyle=solid%s]{c-c}"
1522 	      "("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1523 	      thickness, linecolor_option, blank_fillcolor_option,
1524 	      x_rga, y_rga, x_bas, y_bas, x_lfa, y_lfa);
1525     }
1526     else {
1527       if (Verbose)
1528 	fprintf(tfp, "%% T-bar arrow\n");
1529       fprintf(tfp,
1530 	      "\\psline[linewidth="DBL"%s]{c-c}"
1531 	      "("DBL","DBL")("DBL","DBL")\n",
1532 	      thickness, linecolor_option,
1533 	      x_rga, y_rga, x_lfa, y_lfa);
1534     }
1535     break;
1536 
1537   case PARTIAL_RECTANGLE_ARROW:
1538     if (style == HOLLOW_ARROW) {
1539       if (Verbose)
1540 	fprintf(tfp, "%% cup arrow\n");
1541       fprintf(tfp,
1542 	      "\\psline[linewidth="DBL"%s,fillstyle=solid%s]{c-c}"
1543 	      "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1544 	      thickness, linecolor_option, blank_fillcolor_option,
1545 	      x_lfa, y_lfa, x_lft, y_lft, x_rgt, y_rgt, x_rga, y_rga);
1546     }
1547     else {
1548       if (Verbose)
1549 	fprintf(tfp, "%% cap arrow\n");
1550       fprintf(tfp,
1551 	      "\\psline[linewidth="DBL"%s]{c-c}"
1552 	      "("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
1553 	      thickness, linecolor_option,
1554 	      x_rgt, y_rgt, x_rga, y_rga, x_lfa, y_lfa, x_lft, y_lft);
1555     }
1556     break;
1557 
1558   default:
1559     warn(W_UNKNOWN_ARROW);
1560     goto simple_arrow;
1561   }
1562 }
1563 
1564 /* convenience function to put an arrowhead for a line segment with given end points */
1565 static void
put_arrowhead_on_seg(tl,hd,wid,len,thickness,type,style,pen_color)1566 put_arrowhead_on_seg(tl, hd,
1567                      wid, len, thickness,
1568                      type, style, pen_color)
1569      F_point *tl, *hd;
1570      double wid, len, thickness;
1571      int type, style, pen_color;
1572 {
1573   put_arrowhead((double)hd->x, (double)hd->y,
1574 		(double)(hd->x - tl->x), (double)(hd->y - tl->y),
1575 		wid, len, thickness, type, style, pen_color);
1576 }
1577 
1578 /**********************************************************************/
1579 /* general formatting                                                 */
1580 /**********************************************************************/
1581 
1582 /* format_options flag bits */
1583 #define FO_LINE_STYLE bit(0)
1584 #define FO_PEN_COLOR  bit(1)
1585 #define FO_LINE_TERM  bit(2)
1586 #define FO_ARROWS     bit(3)
1587 #define FO_ARROW_SIZE bit(4)
1588 #define FO_FILL       bit(5)
1589 #define FO_JOIN_STYLE bit(6)
1590 
1591 /* format terminator option strings, one for square
1592    bracket options and one for curly brackets */
1593 static void
format_terminators(opts_sqrb,opts_curb,flags,ch_arrow,arrow,cap_style)1594 format_terminators(opts_sqrb, opts_curb,
1595                    flags,
1596                    ch_arrow, arrow,
1597                    cap_style)
1598      char *opts_sqrb, *opts_curb;
1599      int flags;
1600      int ch_arrow;
1601      F_arrow *arrow;
1602      int cap_style;
1603 {
1604   double inset, length;
1605 
1606   opts_sqrb[0] = opts_curb[0] = '\0';
1607 
1608   if ((flags & FO_ARROWS) && arrow) {
1609 
1610     /* default will be the given arrow character, no inset, specified length */
1611     sprintf(opts_curb, "%c", ch_arrow);
1612     inset = 0.0;
1613     length = arrow->ht/arrow->wid;
1614 
1615     /* draw based on type; those cases that are proper arrows fall
1616        through to a block on the bottom that sets arrow size options;
1617        those that are special cases set opts_sqrb and opts_curb and
1618        return directly */
1619     switch (arrow->type) {
1620     case STICK_ARROW:
1621     simple_arrow:
1622       inset = 1.0;
1623       break;
1624 
1625     case TRIANGLE_HALF_ARROW:
1626       /* closest we can get to a half-arrow is a full arrow */
1627       warn(W_TERMINATOR_ARROW);
1628     case TRIANGLE_ARROW:
1629       break;
1630 
1631     case BISHADE_INDENTED_BUTT_ARROW:
1632     case INDENTED_BUTT_HALF_ARROW:
1633       /* closest we can get to bishade and indented butt arrows is a
1634 	 full indented butt arrow */
1635       warn(W_TERMINATOR_ARROW);
1636     case INDENTED_BUTT_ARROW:
1637       inset = 0.3;
1638       break;
1639 
1640     case POINTED_BUTT_HALF_ARROW:
1641       /* closest we can get to a pointed butt half arrow is a pointed
1642 	 butt full arrow */
1643       warn(W_TERMINATOR_ARROW);
1644     case POINTED_BUTT_ARROW:
1645       inset = -0.3;
1646       /* It looks like Andreas is doing this, but original calc_arrow does not. */
1647       length *= 1.0 / (1.0 - inset);
1648       break;
1649 
1650     case RECTANGLE_ARROW:
1651       /* closest we can get to a rectangle is a diamond */
1652       warn(W_TERMINATOR_ARROW);
1653     case DIAMOND_ARROW:
1654       inset = -1.0;
1655       length *= 1.0 / (1.0 - inset);
1656       break;
1657 
1658     case SEMI_CIRCLE_ARROW:
1659       warn(W_TERMINATOR_ARROW);
1660       /* closest we can get to semicircle is centered disk */
1661       if (flags & FO_ARROW_SIZE)
1662 	sprintf(opts_sqrb, "dotsize="DBL" 2", cm_from_1200ths(arrow->ht));
1663       strcpy(opts_curb, arrow->style == HOLLOW_ARROW ? "o" : "*");
1664       return;
1665 
1666     case CIRCLE_ARROW:
1667       if (flags & FO_ARROW_SIZE)
1668 	sprintf(opts_sqrb, "dotsize="DBL" 2", cm_from_1200ths(arrow->ht));
1669       strcpy(opts_curb, arrow->style == HOLLOW_ARROW ? "oo" : "**");
1670       return;
1671 
1672     case PARTIAL_REVERSE_TRIANGLE_ARROW:
1673       if (arrow->style == HOLLOW_ARROW) {
1674 	/* reverse stick arrow */
1675 	inset = 1;
1676 	sprintf(opts_curb, "%c", (ch_arrow == '<') ? '>' : '<');
1677       }
1678       else {
1679 	/* t-bar centered on end */
1680 	if (flags & FO_ARROW_SIZE)
1681 	  sprintf(opts_sqrb, "tbarsize="DBL" 2", cm_from_1200ths(arrow->wid));
1682 	strcpy(opts_curb, "|");
1683 	return;
1684       }
1685       break;
1686 
1687     case REVERSE_TRIANGLE_ARROW:
1688       inset = 0;
1689       sprintf(opts_curb, "%c", (ch_arrow == '<') ? '>' : '<');
1690       break;
1691 
1692     case PARTIAL_RECTANGLE_ARROW:
1693       /* closest we can get to a cup is a cap */
1694       if (arrow->style == HOLLOW_ARROW)
1695 	warn(W_TERMINATOR_ARROW);
1696       if (flags & FO_ARROW_SIZE)
1697 	sprintf(opts_sqrb, "tbarsize="DBL" 2,bracketlength="DBL,
1698 		cm_from_1200ths(arrow->wid),
1699 		arrow->ht/arrow->wid);
1700       sprintf(opts_curb, "%c", (ch_arrow == '<') ? '[' : ']');
1701       return;
1702 
1703     default:
1704       warn(W_UNKNOWN_ARROW);
1705       goto simple_arrow;
1706     }
1707     if (flags & FO_ARROW_SIZE) {
1708 
1709       sprintf(opts_sqrb, "arrowsize="DBL" 2,arrowlength=%.5lf,arrowinset=%.5lf",
1710 	      cm_from_1200ths(arrow->wid), length, inset);
1711       if (arrow->style == HOLLOW_ARROW) {
1712         /* warn user hollow arrows require additional package pstricks-add */
1713         warn(W_HOLLOW_ARROW);
1714         append_to_comma_list(&opts_sqrb, "ArrowFill=false");
1715       }
1716     }
1717   }
1718   else {
1719     strcpy(opts_curb,
1720 	   cap_style == 1 ? "c" : /* round */
1721 	   cap_style == 2 ? "C" : /* extended square */
1722 	   "");  /* butt (flush square) */
1723   }
1724   /* thickness is ignored */
1725 }
1726 
1727 /* Format an options string that optionally takes care of
1728 
1729 line style
1730 pen color
1731 line terminators
1732 line join
1733 arrows (only if line terminators are turned on)
1734 arrow size (if user didn't ask for default sizes)
1735 fill style, fill color and/or hatch color
1736 
1737 This is a little nasty because some of pstricks options go in square brackets and others
1738 in curly brackets, and early versions required \pstverb commands to set line join.
1739 For this purpose, prefix and postfix are used.  So this procedure formats all the
1740 strings and combines the brackets at the end.  The square brackets options can be given
1741 initial contents by providing sqrb_init.  Prefix and postfix are set and returned
1742 separately. */
1743 static char*
format_options(options,prefix,postfix,sqrb_init,flags,thickness,style,style_val,pen_color,join_style,cap_style,fill_style,fill_color,back_arrow,fore_arrow,hatch_angle_offset)1744 format_options(options, prefix, postfix,
1745                sqrb_init, flags, thickness, style, style_val,
1746                pen_color, join_style, cap_style, fill_style, fill_color,
1747                back_arrow, fore_arrow,
1748                hatch_angle_offset)
1749      char *options, *prefix, *postfix, *sqrb_init;
1750      int flags, thickness, style;
1751      double style_val;
1752      int pen_color, join_style, cap_style, fill_style, fill_color;
1753      F_arrow *back_arrow, *fore_arrow;
1754      double hatch_angle_offset;
1755 {
1756   char tmps[256], tmps_alt[256],
1757     tmpc[256],
1758     opts_sqrb[256], /* square bracket options */
1759     opts_curb[256]; /* curly bracket options */
1760   char *p_sqrb;
1761 
1762   /* set up square bracket option buffer */
1763   if (sqrb_init)
1764     strcpy(opts_sqrb, sqrb_init);
1765   else
1766     opts_sqrb[0] = '\0';
1767   p_sqrb = opts_sqrb;
1768 
1769   /* set up curly bracket option buffer */
1770   opts_curb[0] = '\0';
1771 
1772   if (flags & FO_LINE_STYLE) {
1773 
1774     /* fig allows filled lines where the line has zero width;
1775        the intent is to draw the filling with no line at all
1776        pstricks always draws a line even if linewidth is zero
1777        argh...
1778        so we will translate to a pseudo-line style -2, meaning
1779        linestyle=none, which is how PSTricks does filling with
1780        no line */
1781     if (thickness == 0) {
1782       style = -2;
1783     }
1784     else {
1785       sprintf(tmps, "linewidth="DBL"",
1786 	      cm_from_1200ths(thickness) * Line_weight);
1787       append_to_comma_list(&p_sqrb, tmps);
1788     }
1789 
1790     switch (style) {
1791 
1792     case -2:
1793       append_to_comma_list(&p_sqrb, "linestyle=none");
1794       break;
1795 
1796     case -1: /* default; use solid */
1797     case SOLID_LINE:
1798       break;
1799 
1800     case DOTTED_LINE:
1801       sprintf(tmps, "linestyle=dotted,dotsep="DBL"",
1802 	      cm_from_80ths(style_val));
1803       append_to_comma_list(&p_sqrb, tmps);
1804       break;
1805 
1806     case DASH_LINE:
1807       sprintf(tmps, "linestyle=dashed,dash="DBL" "DBL"",
1808 	      cm_from_80ths(style_val),
1809 	      cm_from_80ths(style_val));
1810       append_to_comma_list(&p_sqrb, tmps);
1811       break;
1812 
1813     case DASH_DOT_LINE:    /* normal dashes with small spaces */
1814       warn(W_DASH_DOT);
1815       sprintf(tmps, "linestyle=dashed,dash="DBL" "DBL" "DBL" "DBL"",
1816 	      cm_from_80ths(style_val),
1817 	      cm_from_80ths(style_val)/2,
1818 	      2*cm_from_1200ths(thickness) * Line_weight,
1819 	      cm_from_80ths(style_val)/2);
1820       append_to_comma_list(&p_sqrb, tmps);
1821       break;
1822 
1823     case DASH_2_DOTS_LINE: /* small dashes with normal spaces */
1824       warn(W_DASH_DOTS);
1825       sprintf(tmps, "linestyle=dashed,dash="DBL" "DBL" "DBL" "DBL" "DBL" "DBL"",
1826 	      cm_from_80ths(style_val),
1827 	      cm_from_80ths(style_val)/3,
1828 	      2*cm_from_1200ths(thickness) * Line_weight,
1829 	      cm_from_80ths(style_val)/3,
1830 	      2*cm_from_1200ths(thickness) * Line_weight,
1831 	      cm_from_80ths(style_val)/3);
1832       append_to_comma_list(&p_sqrb, tmps);
1833       break;
1834 
1835     case DASH_3_DOTS_LINE: /* small dashes with small spaces */
1836       warn(W_DASH_DOTS);
1837       sprintf(tmps, "linestyle=dashed,dash="DBL" "DBL" "DBL" "DBL" "DBL" "DBL" "DBL" "DBL,
1838 	      cm_from_80ths(style_val),
1839 	      cm_from_80ths(style_val)/4,
1840 	      2*cm_from_1200ths(thickness) * Line_weight,
1841 	      cm_from_80ths(style_val)/4,
1842 	      2*cm_from_1200ths(thickness) * Line_weight,
1843 	      cm_from_80ths(style_val)/4,
1844 	      2*cm_from_1200ths(thickness) * Line_weight,
1845 	      cm_from_80ths(style_val)/4);
1846       append_to_comma_list(&p_sqrb, tmps);
1847       break;
1848 
1849     default:
1850       fprintf(stderr, "bad line style (%d)\n", style);
1851       exit(1);
1852     }
1853   }
1854 
1855   /* add linejoin in one of two ways, which depend on PSTricks
1856      version we're talking to; old way was with \pstverb new is
1857      with [linejoin=N] */
1858   if (prefix && postfix) {
1859     if ((flags & FO_JOIN_STYLE) != 0 && join_style > 0) {
1860 
1861       if (Pst_version->n_updates == 0)
1862         warn(W_LINEJOIN);
1863 
1864       switch (Linejoin) {
1865       case LJ_PSTVERB:
1866 	sprintf(prefix,  "\\pstVerb{%d setlinejoin}%%\n", join_style);
1867 	sprintf(postfix, "\\pstVerb{0 setlinejoin}%%\n");
1868 	break;
1869       case LJ_PSTOPTION:
1870 	prefix[0] = postfix[0] = '\0';
1871 	sprintf(tmps,  "linejoin=%d", join_style);
1872 	append_to_comma_list(&p_sqrb, tmps);
1873 	break;
1874       default:
1875 	fprintf(stderr, "bad Linejoin value %d\n", Linejoin);
1876 	break;
1877       }
1878     }
1879     else
1880       prefix[0] = postfix[0] = '\0';
1881   }
1882 
1883   /* add cap style and optionally the arrows */
1884   if (flags & FO_LINE_TERM) {
1885     format_terminators(tmps, tmpc, flags, '<', back_arrow, cap_style);
1886     strcat(opts_curb, tmpc);
1887     strcat(opts_curb, "-");
1888     format_terminators(tmps_alt, tmpc, flags, '>', fore_arrow, cap_style);
1889     strcat(opts_curb, tmpc);
1890     /* forward arrow parameters take precedence over back; can't have both */
1891     append_to_comma_list(&p_sqrb, tmps_alt[0] == '\0' ? tmps : tmps_alt);
1892   }
1893 
1894   /* deal with pen color */
1895   if ((flags & FO_PEN_COLOR) && pen_color != -1) {
1896     sprintf(tmps, "linecolor=%s", color_name_after_declare_color(pen_color));
1897     append_to_comma_list(&p_sqrb, tmps);
1898   }
1899 
1900   if ((flags & FO_FILL) && fill_style != -1) {
1901     if (fill_color == CT_WHITE && fill_style <= 20)
1902       /* black to white gray fill */
1903       sprintf(tmps, "fillstyle=solid,fillcolor=%s", gray_name_after_declare_gray(fill_style));
1904     else if ((fill_color == CT_BLACK || fill_color == -1) && fill_style <= 20)
1905       /* white to black gray fill */
1906       sprintf(tmps, "fillstyle=solid,fillcolor=%s", gray_name_after_declare_gray(20 - fill_style));
1907     else if (fill_style <= 40)
1908       /* shade or tint fill */
1909       sprintf(tmps, "fillstyle=solid,fillcolor=%s",
1910 	      shade_or_tint_name_after_declare_color(tmpc, fill_style, fill_color));
1911     else {
1912       char *type = 0, *ps;
1913       int angle = 0;
1914 
1915       /* hatch pattern */
1916       switch (fill_style) {
1917       case 41: /* 30 degrees left diagonal */
1918 	type = "hlines";
1919 	angle = 30;
1920 	break;
1921       case 42: /* 30 degrees right diagonal */
1922 	type = "hlines";
1923 	angle = -30;
1924 	break;
1925       case 43: /* 30 degree crosshatch */
1926 	type = "crosshatch";
1927 	angle = 30;
1928 	break;
1929       case 44: /* 45 degrees left diagonal */
1930 	type = "vlines";
1931 	angle = 45;
1932 	break;
1933       case 45: /* 45 degrees right diagonal */
1934 	type = "hlines";
1935 	angle = -45;
1936 	break;
1937       case 46: /* 45 degree crosshatch */
1938 	type = "crosshatch";
1939 	angle = 45;
1940 	break;
1941       case 49: /* horizontal lines */
1942 	type = "hlines";
1943 	angle = 0;
1944 	break;
1945       case 50: /* vertical lines */
1946 	type = "vlines";
1947 	angle = 0;
1948 	break;
1949       case 51: /* horizontal/vertical degree crosshatch */
1950 	type = "crosshatch";
1951 	angle = 0;
1952 	break;
1953       default:
1954 	warn(W_HATCH_PATTERN);
1955 	type = "crosshatch";
1956 	angle = 10;
1957 	break;
1958       }
1959       /* build up fill and hatch color strings, accounting for default colors */
1960       tmps[0] = '\0';
1961       ps = tmps;
1962       if (fill_color != -1) {
1963         sprintf(tmps_alt, "fillcolor=%s", color_name_after_declare_color(fill_color));
1964         append_to_comma_list(&ps, tmps_alt);
1965       }
1966       if (pen_color != -1) {
1967         sprintf(tmps_alt, "hatchcolor=%s", color_name_after_declare_color(pen_color));
1968         append_to_comma_list(&ps, tmps_alt);
1969       }
1970       /* negative sign on angle for fig's strange clockwise system */
1971       sprintf(tmps_alt, "fillstyle=%s*,hatchangle=%.2lf", type, -(angle + hatch_angle_offset));
1972       append_to_comma_list(&ps, tmps_alt);
1973     }
1974     append_to_comma_list(&p_sqrb, tmps);
1975   }
1976 
1977   if (opts_curb[0] && opts_sqrb[0])
1978     sprintf(options, "[%s]{%s}",  opts_sqrb, opts_curb);
1979   else if (opts_sqrb[0])
1980     sprintf(options, "[%s]",  opts_sqrb);
1981   else if (opts_curb[0])
1982     sprintf(options, "{%s}", opts_curb);
1983   else
1984     options[0] = '\0';
1985 
1986   return options;
1987 }
1988 
1989 /* do conversions for non-eps files; may try to
1990    run bmeps if user asked for it; else we just
1991    convert file paths to place where the user
1992    should put the eps-converted files him/herself
1993 
1994    must take some care that the conversion folder
1995    doesn't have name clashes and that conversion
1996    for a given source path is done only once */
do_eps_conversion(eps_path,src)1997 void do_eps_conversion(eps_path, src)
1998      char *eps_path, *src;
1999 {
2000   int i, ibmeps, errval, base_index, islash, iext;
2001   char buf[256], uniqified_base[256], *base, *ext;
2002   STRING_TABLE_NODE *stn;
2003 
2004   static STRING_TABLE converted[1], base_names[1];
2005 
2006 #define BMEPS_PNG 0
2007 #define BMEPS_JPG 1
2008 #define BMEPS_PNM 2
2009 #define BMEPS_TIF 3
2010 
2011   static char *bmeps_ext_tbl[] = {
2012     "png",
2013     "jpg",
2014     "pnm",
2015     "tif",
2016   };
2017 
2018   static struct {
2019     char *ext;
2020     int bmeps_type;
2021   } ext_tbl[] = {
2022     { "png",  BMEPS_PNG},
2023     { "jpg",  BMEPS_JPG},
2024     { "jpeg", BMEPS_JPG},
2025     { "pnm",  BMEPS_PNM},
2026     { "tif",  BMEPS_TIF},
2027     { "tiff", BMEPS_TIF},
2028   };
2029 
2030   /* if we've already converted this one,
2031      return the eps path for it */
2032   stn = string_lookup_val(converted, src);
2033   if (stn) {
2034     strcpy(eps_path, (char*)stn->val.vp);
2035     return;
2036   }
2037 
2038   islash = -1;
2039   for (i = 0; src[i]; i++) {
2040     switch (src[i]) {
2041     case '\\':
2042       warn(W_SLASH_CONVERT);
2043       islash = i;
2044       buf[i] = '/';
2045       break;
2046     case '/':
2047       islash = i;
2048       buf[i] = src[i];
2049       break;
2050     case ' ':
2051     case '>':
2052     case '<':
2053     case '|':
2054     case '$':
2055     case '%':
2056     case '*':
2057     case '?':
2058       warn(W_FN_CHARS);
2059       buf[i] = src[i];
2060       break;
2061     default:
2062       buf[i] = src[i];
2063       break;
2064     }
2065   }
2066   buf[i] = '\0';
2067 
2068   /* get base file name from slash location */
2069   base = (islash == -1) ? buf : &buf[islash + 1];
2070 
2071   /* find and cut off extension */
2072   iext = strlen(buf);
2073   for (i = iext - 1; i > islash; i--) {
2074     if (buf[i] == '.') {
2075       buf[i] = '\0';
2076       iext = i + 1;
2077       break;
2078     }
2079   }
2080   /* lowercase the extension */
2081   for (i = iext; buf[i]; i++) {
2082     if ('A' <= buf[i] && buf[i] <= 'Z')
2083       buf[i] += 'a' - 'A';
2084   }
2085 
2086   /* all done ith extension */
2087   ext = &buf[iext];
2088 
2089   /* build new path to conversion directory
2090      if the base is already in the table, then
2091      the name is already in use for some other
2092      converted path with the same base, so
2093      uniqify the base name for this conversion */
2094   stn = string_lookup_val(base_names, base);
2095   if (stn) {
2096     base_index = ++stn->val.i;
2097     sprintf(uniqified_base, "%s-%03d", base, base_index);
2098   }
2099   else {
2100     strcpy(uniqified_base, base);
2101   }
2102   sprintf(eps_path, "%s%s", Pic_convert_dir, uniqified_base);
2103 
2104   /* now we can fill in the tables; we act as if conversion
2105      succeeded even if it doesn't because the LaTeX file is useless
2106      unless user can get conversion to eps done him/herself */
2107   stn = add_string(converted, src);
2108   stn->val.vp = xstrdup(eps_path);
2109   add_string(base_names, uniqified_base);
2110 
2111   if (Pic_convert_p) {
2112     /* see if it's a type we know about */
2113     ibmeps = -1;
2114     for (i = 0; i < ARRAY_SIZE(ext_tbl); i++) {
2115       if (strcmp(ext_tbl[i].ext, ext) == 0) {
2116         ibmeps = i;
2117         break;
2118       }
2119     }
2120     if (ibmeps == -1)
2121       warn(W_UNK_PIC_TYPE);
2122     else {
2123       char cmd[1024];
2124       /* try to run bmeps */
2125       sprintf(cmd, "bmeps -c -t %s \"%s\" \"%s.eps\"",
2126 	      bmeps_ext_tbl[ibmeps], src, eps_path);
2127       if (Verbose)
2128         fprintf(stderr, "running system(%s): ", cmd);
2129       errval = system(cmd);
2130       if (Verbose)
2131         fprintf(stderr, "returned %d\n", errval);
2132       if (errval == -1)
2133         warn(W_SYSTEM_EXEC);
2134       else if (errval != 0)
2135         warn(W_BMEPS_ERR);
2136       else
2137         warn(W_PIC_CONVERT);
2138     }
2139   }
2140   else {
2141     warn(W_EPS_NEEDED);
2142   }
2143 }
2144 
2145 /**********************************************************************/
2146 /* line drawing                                                       */
2147 /**********************************************************************/
2148 
2149 void
genpstrx_line(line)2150 genpstrx_line(line)
2151      F_line *line;
2152 {
2153   F_point *p;
2154   int llx, lly, urx, ury;
2155   char options[256], file_name[256], prefix[256], postfix[256];
2156 
2157   /* print any comments prefixed with "%" */
2158   print_comments("%% ",line->comments,"");
2159 
2160   if (!line->points)
2161     return;
2162 
2163   /* adjust for bounding box and top<->bottom flip */
2164   for (p = line->points; p; p = p->next)
2165     convert_pt_to_image_bb_in_place(p);
2166 
2167   /* if only one point, draw a dot */
2168   if (!line->points->next ||
2169       /* polygons always have the first point repeated */
2170       (line->type == T_POLYGON && !line->points->next->next)) {
2171     if (Verbose)
2172       fprintf(tfp, "%% line, length zero\n");
2173     fprintf(tfp, "\\psdots[dotsize="DBL"]("DBL","DBL")\n",
2174 	    cm_from_1200ths(line->thickness) * Line_weight / 2,
2175 	    cm_from_1200ths(line->points->x),
2176 	    cm_from_1200ths(line->points->y));
2177     return;
2178   }
2179 
2180   switch (line->type) {
2181 
2182   case T_ARC_BOX: {
2183     int radius;
2184 
2185     if (Verbose)
2186       fprintf(tfp, "%% arc box\n");
2187 
2188     find_bounding_box_of_points(&llx, &lly, &urx, &ury, line->points);
2189 
2190     radius = line->radius;		/* radius of the corner */
2191     fold_min_int(&radius, (urx - llx) / 2);
2192     fold_min_int(&radius, (ury - lly) / 2);
2193     sprintf(options, "cornersize=absolute,linearc="DBL"", cm_from_1200ths(radius));
2194 
2195     format_options(options, 0, 0, options,
2196 		   FO_LINE_STYLE | FO_PEN_COLOR | FO_FILL,
2197 		   line->thickness, line->style, line->style_val,
2198 		   line->pen_color, line->join_style, line->cap_style,
2199 		   line->fill_style, line->fill_color,
2200 		   0,0,0.0);
2201 
2202     fprintf(tfp, "\\psframe%s("DBL","DBL")("DBL","DBL")\n",
2203 	    options,
2204 	    cm_from_1200ths(llx), cm_from_1200ths(lly),
2205 	    cm_from_1200ths(urx), cm_from_1200ths(ury));
2206   } break;
2207 
2208   case T_PIC_BOX: {
2209 
2210     int rot, x0, y0, wd, ht;
2211 
2212     /* rotation angle ends up encoded in diagonal
2213        x and y order; use these to decode them */
2214     static int rot_from_signs[2][2] = {
2215       {  90,   0 },  /* x normal   { y normal, inverted } */
2216       { 180, -90 }}; /* x inverted { y normal, inverted } */
2217     static int flipped_rot_from_signs[2][2] = {
2218       {   0,  90 },  /* x normal   { y normal, inverted } */
2219       { -90, 180 }}; /* x inverted { y normal, inverted } */
2220 
2221     if (Verbose)
2222       fprintf(tfp, "%% pic box\n");
2223     warn(W_PIC);
2224 
2225     find_bounding_box_of_points(&llx, &lly, &urx, &ury, line->points);
2226 
2227     x0 = llx;
2228     y0 = lly;
2229     wd = urx - llx;
2230     ht = ury - lly;
2231     if (line->pic->flipped) {
2232       /* a flip is equivalent to a 90d rotation
2233 	 and a mirror about the x-axis */
2234       rot = flipped_rot_from_signs
2235 	[line->points->next->next->x < line->points->x]
2236         [line->points->next->next->y < line->points->y];
2237       ht = -ht;
2238     }
2239     else {
2240       rot = rot_from_signs
2241 	[line->points->next->next->x < line->points->x]
2242         [line->points->next->next->y < line->points->y];
2243     }
2244 
2245     /* format the pic file name, but this may also do a conversion to eps! */
2246     do_eps_conversion(file_name, line->pic->file);
2247 
2248     fprintf(tfp,
2249 	    "\\rput[bl]("DBL","DBL")"
2250 	    "{\\includegraphics[origin=c,angle=%d,width=%.4lf\\unit,totalheight=%.4lf\\unit]{%s}}%%\n",
2251 	    cm_from_1200ths(x0), cm_from_1200ths(y0),
2252 	    rot,
2253 	    cm_from_1200ths(wd), cm_from_1200ths(ht),
2254 	    file_name);
2255   } break;
2256 
2257   case T_BOX:
2258     if (Verbose)
2259       fprintf(tfp, "%% box\n");
2260 
2261     find_bounding_box_of_points(&llx, &lly, &urx, &ury, line->points);
2262 
2263     format_options(options, prefix, postfix, 0,
2264 		   FO_LINE_STYLE | FO_JOIN_STYLE | FO_PEN_COLOR | FO_FILL,
2265 		   line->thickness, line->style, line->style_val,
2266 		   line->pen_color, line->join_style, line->cap_style,
2267 		   line->fill_style, line->fill_color,
2268 		   0,0,0.0);
2269 
2270     fprintf(tfp, "%s\\psframe%s("DBL","DBL")("DBL","DBL")\n%s",
2271 	    prefix,
2272 	    options,
2273 	    cm_from_1200ths(llx), cm_from_1200ths(lly),
2274 	    cm_from_1200ths(urx), cm_from_1200ths(ury),
2275 	    postfix);
2276     break;
2277 
2278   case T_POLYLINE:
2279     if (Verbose)
2280       fprintf(tfp, "%% polyline\n");
2281 
2282 #define FO_POLYLINE_STD (FO_LINE_STYLE | FO_JOIN_STYLE | FO_PEN_COLOR | FO_FILL | FO_LINE_TERM)
2283 
2284     format_options(options, prefix, postfix, 0,
2285 		   Arrows == A_XFIG      ?  FO_POLYLINE_STD :
2286 		   Arrows == A_PSTRICKS  ? (FO_POLYLINE_STD | FO_ARROWS | FO_ARROW_SIZE) :
2287 		   /* default */           (FO_POLYLINE_STD | FO_ARROWS),
2288 		   line->thickness, line->style, line->style_val,
2289 		   line->pen_color, line->join_style, line->cap_style,
2290 		   line->fill_style, line->fill_color,
2291 		   line->back_arrow,line->for_arrow, 0.0);
2292 
2293     fprintf(tfp, "%s\\psline%s", prefix, options);
2294     put_points(line->points, PP_NORMAL);
2295     fprintf(tfp, "%s", postfix);
2296 
2297     if (Arrows == A_XFIG) {
2298       if (line->back_arrow)
2299 	put_arrowhead_on_seg(line->points->next, line->points,
2300 			     line->back_arrow->wid, line->back_arrow->ht,
2301 			     max_dbl(line->back_arrow->thickness, (double)line->thickness),
2302 			     line->back_arrow->type, line->back_arrow->style,
2303 			     line->pen_color);
2304       if (line->for_arrow) {
2305 	for (p = line->points; p->next->next; p = p->next)
2306 	  /* skip */ ;
2307 	put_arrowhead_on_seg(p, p->next,
2308 			     line->for_arrow->wid, line->for_arrow->ht,
2309 			     max_dbl(line->for_arrow->thickness, (double)line->thickness),
2310 			     line->for_arrow->type, line->for_arrow->style,
2311 			     line->pen_color);
2312       }
2313     }
2314     break;
2315 
2316   case T_POLYGON:
2317     if (Verbose)
2318       fprintf(tfp, "%% polygon\n");
2319 
2320     /* if >2 points (3 with repeat at end), draw a polygon, else a line */
2321     if (line->points->next->next->next) {
2322       format_options(options, prefix, postfix, 0,
2323 		     FO_LINE_STYLE | FO_JOIN_STYLE | FO_PEN_COLOR | FO_FILL,
2324 		     line->thickness, line->style, line->style_val,
2325 		     line->pen_color, line->join_style, line->cap_style, line->fill_style, line->fill_color,
2326 		     0,0,0.0);
2327       fprintf(tfp, "%s\\pspolygon%s", prefix, options);
2328     }
2329     else {
2330       format_options(options, prefix, postfix, 0,
2331 		     FO_LINE_STYLE | FO_PEN_COLOR,  /* no JOIN_STYLE clears postfix */
2332 		     line->thickness, line->style, line->style_val,
2333 		     line->pen_color, line->join_style, line->cap_style, line->fill_style, line->fill_color,
2334 		     0,0, 0.0);
2335       fprintf(tfp, "\\psline%s", options);
2336     }
2337     put_points(line->points, PP_SKIP_LAST);
2338     fprintf(tfp, "%s", postfix);
2339     break;
2340   }
2341 }
2342 
2343 /**********************************************************************/
2344 /* spline drawing                                                     */
2345 /**********************************************************************/
2346 
2347 static void
put_bezier(options,p1x,p1y,p2x,p2y,p3x,p3y)2348 put_bezier(options,
2349            p1x, p1y, /* 3 user control points */
2350            p2x, p2y,
2351            p3x, p3y)
2352      char *options;
2353      double p1x, p1y, p2x, p2y, p3x, p3y;
2354 {
2355   fprintf(tfp, "\\psbezier%s("DBL","DBL")("DBL","DBL")("DBL","DBL")("DBL","DBL")\n",
2356 	  options,
2357 	  p1x, p1y,
2358 	  p1x + 2.0/3.0 * (p2x - p1x), p1y + 2.0/3.0 * (p2y - p1y),
2359 	  p3x + 2.0/3.0 * (p2x - p3x), p3y + 2.0/3.0 * (p2y - p3y),
2360 	  p3x, p3y);
2361 }
2362 
2363 void
genpstrx_spline(spline)2364 genpstrx_spline(spline)
2365      F_spline *spline;
2366 {
2367   char options[256];
2368   F_point *p, *q, *r;
2369   int flags;
2370 
2371   if (!spline->points)
2372     return;
2373 
2374   /* warn user this code is completely untested */
2375   warn(W_OLD_SPLNE);
2376 
2377   /* adjust for bounding box and top<->bottom flip */
2378   for (p = spline->points; p; p = p->next)
2379     convert_pt_to_image_bb_in_place(p);
2380 
2381   /* if only one point, draw a dot */
2382   if (!spline->points->next) {
2383     if (Verbose)
2384       fprintf(tfp, "%% spline, length zero\n");
2385     fprintf(tfp, "\\psdots[dotsize="DBL"]("DBL","DBL")\n",
2386 	    cm_from_1200ths(spline->thickness) * Line_weight / 2,
2387 	    cm_from_1200ths(spline->points->x),
2388 	    cm_from_1200ths(spline->points->y));
2389     return;
2390   }
2391 
2392   /* if two points, draw a line */
2393   if (!spline->points->next->next) {
2394 
2395 #define FO_SPLINE_STD (FO_LINE_STYLE | FO_PEN_COLOR | FO_LINE_TERM)
2396 
2397     format_options(options, 0, 0, 0,
2398 		   Arrows == A_XFIG      ?  FO_SPLINE_STD :
2399 		   Arrows == A_PSTRICKS  ? (FO_SPLINE_STD | FO_ARROWS | FO_ARROW_SIZE) :
2400 		   /* default */           (FO_SPLINE_STD | FO_ARROWS),
2401 		   spline->thickness, spline->style, spline->style_val,
2402 		   spline->pen_color, 0, spline->cap_style, spline->fill_style, spline->fill_color,
2403 		   spline->back_arrow,spline->for_arrow, 0.0);
2404 
2405     fprintf(tfp, "\\psline%s", options);
2406     put_points(spline->points, PP_NORMAL);
2407 
2408     if (Arrows == A_XFIG) {
2409       if (spline->back_arrow)
2410         put_arrowhead_on_seg(spline->points->next, spline->points,
2411 			     spline->back_arrow->wid, spline->back_arrow->ht,
2412 			     max_dbl(spline->back_arrow->thickness, (double)spline->thickness),
2413 			     spline->back_arrow->type, spline->back_arrow->style,
2414 			     spline->pen_color);
2415       if (spline->for_arrow) {
2416         for (p = spline->points; p->next->next; p = p->next)
2417           /* skip */ ;
2418         put_arrowhead_on_seg(p, p->next,
2419 			     spline->for_arrow->wid, spline->for_arrow->ht,
2420 			     max_dbl(spline->for_arrow->thickness, (double)spline->thickness),
2421 			     spline->for_arrow->type, spline->for_arrow->style,
2422 			     spline->pen_color);
2423       }
2424     }
2425     return;
2426   }
2427 
2428   /* general case */
2429   flags = closed_spline(spline) ? (FO_LINE_STYLE | FO_PEN_COLOR | FO_FILL) :
2430     /* open spline flags */
2431     Arrows == A_XFIG      ?  FO_SPLINE_STD :
2432     Arrows == A_PSTRICKS  ? (FO_SPLINE_STD | FO_ARROWS | FO_ARROW_SIZE) :
2433     /* default */           (FO_SPLINE_STD | FO_ARROWS),
2434 
2435     format_options(options, 0, 0, 0, flags,
2436 		   spline->thickness, spline->style, spline->style_val,
2437 		   spline->pen_color, 0, spline->cap_style, spline->fill_style, spline->fill_color,
2438 		   spline->back_arrow,spline->for_arrow, 0.0);
2439 
2440   if (int_spline(spline)) {
2441     /* interpolating spline; just use pscurve */
2442     if (closed_spline(spline)) {
2443       fprintf(tfp, "\\psecurve%s", options);
2444       put_points(spline->points, PP_REPEAT_2);
2445     }
2446     else {
2447       fprintf(tfp, "\\pscurve%s", options);
2448       put_points(spline->points, PP_NORMAL);
2449     }
2450   }
2451   else {
2452     /* approximating spline
2453        Output is a set of end-to-end joined bezier curves. Control
2454        polygons are computed between midpoints of control polygon segments.
2455        The inner vertices are 2/3 of the way to the user control vertex. */
2456     if (closed_spline(spline)) {
2457       /* get last 2 points into q (last) and r (second last) */
2458       for (r = 0, q = spline->points;
2459 	   q->next;
2460 	   r = q, q = q->next)
2461         /* skip */ ;
2462       /* Points pqr are always a series of three adjacent points on the control poly.
2463 	 This loop will cover the last two vertices twice, which closes the curve. */
2464       for (p = spline->points; p != 0; r = q, q = p, p = p->next) {
2465         put_bezier(options,
2466 		   1.0/2.0 * cm_from_1200ths(r->x + q->x), 1.0/2.0 * cm_from_1200ths(r->y + q->y),
2467 		   cm_from_1200ths(q->x),                  cm_from_1200ths(q->y),
2468 		   1.0/2.0 * cm_from_1200ths(q->x + p->x), 1.0/2.0 * cm_from_1200ths(q->y + p->y));
2469       }
2470     }
2471     else {
2472       /* make pqr the first three points */
2473       r = spline->points;
2474       q = r->next;
2475       p = q->next;
2476 
2477       /* first point and last points handled specially so curve hits them */
2478       put_bezier(options,
2479 		 cm_from_1200ths(r->x),                  cm_from_1200ths(r->y),
2480 		 cm_from_1200ths(q->x),                  cm_from_1200ths(q->y),
2481 		 1.0/2.0 * cm_from_1200ths(q->x + p->x), 1.0/2.0 * cm_from_1200ths(q->y + p->y));
2482 
2483       for (r = q, q = p, p = p->next; p->next != 0; r = q, q = p, p = p->next) {
2484         put_bezier(options,
2485 		   1.0/2.0 * cm_from_1200ths(r->x + q->x), 1.0/2.0 * cm_from_1200ths(r->y + q->y),
2486 		   cm_from_1200ths(q->x),                  cm_from_1200ths(q->y),
2487 		   1.0/2.0 * cm_from_1200ths(q->x + p->x), 1.0/2.0 * cm_from_1200ths(q->y + p->y));
2488       }
2489 
2490       put_bezier(options,
2491 		 1.0/2.0 * cm_from_1200ths(r->x + q->x), cm_from_1200ths(r->y + q->y),
2492 		 cm_from_1200ths(q->x),        cm_from_1200ths(q->y),
2493 		 cm_from_1200ths(p->x),        cm_from_1200ths(p->y));
2494     }
2495   }
2496 }
2497 
2498 void
genpstrx_ellipse(ellipse)2499 genpstrx_ellipse(ellipse)
2500      F_ellipse *ellipse;
2501 {
2502   int x, y;
2503   char options[256];
2504 
2505   convert_xy_to_image_bb(&x, &y, ellipse->center.x, ellipse->center.y);
2506 
2507   /* note the kludge to compensate for rotation in hatch pattern angle */
2508   format_options(options, 0, 0, 0,
2509 		 FO_LINE_STYLE | FO_PEN_COLOR | FO_FILL,
2510 		 ellipse->thickness, ellipse->style, ellipse->style_val,
2511 		 ellipse->pen_color, 0, 0, ellipse->fill_style, ellipse->fill_color,
2512 		 0,0, ellipse->angle * (180.0 / PI));
2513 
2514   /* cull out circles as a special case */
2515   if (ellipse->radiuses.x == ellipse->radiuses.y) {
2516     fprintf(tfp, "\\pscircle%s("DBL","DBL"){"DBL"}%%\n",
2517 	    options,
2518 	    cm_from_1200ths(x), cm_from_1200ths(y),
2519 	    cm_from_1200ths(ellipse->radiuses.x));
2520   }
2521   else if (ellipse->angle == 0) {
2522     fprintf(tfp, "\\psellipse%s("DBL","DBL")("DBL","DBL")\n",
2523 	    options,
2524 	    cm_from_1200ths(x), cm_from_1200ths(y),
2525 	    cm_from_1200ths(ellipse->radiuses.x), cm_from_1200ths(ellipse->radiuses.y));
2526   }
2527   else {
2528     if (ellipse->fill_style > 40)
2529       warn(W_ELLIPSE_HATCH);
2530     fprintf(tfp, "\\rput{"DBL"}("DBL","DBL"){\\psellipse%s(0,0)("DBL","DBL")}%%\n",
2531 	    ellipse->angle * (180.0 / PI),
2532 	    cm_from_1200ths(x), cm_from_1200ths(y),
2533 	    options,
2534 	    cm_from_1200ths(ellipse->radiuses.x), cm_from_1200ths(ellipse->radiuses.y));
2535   }
2536 }
2537 
2538 /**********************************************************************/
2539 /* text drawing                                                       */
2540 /**********************************************************************/
2541 
2542 static char *
ref_pt_str_from_text_type(int type)2543 ref_pt_str_from_text_type(int type)
2544 {
2545   switch (type) {
2546   case T_LEFT_JUSTIFIED:
2547   case DEFAULT:
2548     return "lb";
2549   case T_CENTER_JUSTIFIED:
2550     return "b";
2551   case T_RIGHT_JUSTIFIED:
2552     return "rb";
2553   default:
2554     fprintf(stderr, "unknown text position (%d)\n", type);
2555     exit(1);
2556   }
2557 }
2558 
2559 static char *
just_str_from_text_type(int type)2560 just_str_from_text_type(int type)
2561 {
2562   switch (type) {
2563   case T_LEFT_JUSTIFIED:
2564   case DEFAULT:
2565     return "l";
2566   case T_CENTER_JUSTIFIED:
2567     return "c";
2568   case T_RIGHT_JUSTIFIED:
2569     return "r";
2570   default:
2571     fprintf(stderr, "unknown text position (%d)\n", type);
2572     exit(1);
2573   }
2574 }
2575 
2576 static char longest_translation[] = "$\\backslash$";
2577 static struct x_t {
2578   int ch;
2579   char *translation;
2580 } translation_table[] = {
2581   {'\\', longest_translation },
2582   { '{', "$\\{$" },
2583   { '}', "$\\}$" },
2584   { '>', "$>$" },
2585   { '<', "$<$" },
2586   { '^', "\\^{}" },
2587   { '~', "\\~{}" },
2588   { '$', "\\$" },
2589   { '&', "\\&" },
2590   { '#', "\\#" },
2591   { '_', "\\_" },
2592   { '%', "\\%" },
2593   {'\n', "\\\\\n" },
2594   {   0, 0 }
2595 };
2596 
2597 void
translate_tex_specials(char * buf,int ch)2598 translate_tex_specials(char *buf, int ch)
2599 {
2600   struct x_t *p;
2601 
2602   for (p = translation_table; p->ch; p++)
2603     if (p->ch == ch) {
2604       strcpy(buf, p->translation);
2605       return;
2606     }
2607   buf[0] = ch;
2608   buf[1] = '\0';
2609 }
2610 
2611 static char *
fmt_text_str(char * buf,int buf_len,char * str,int type)2612 fmt_text_str(char *buf, int buf_len, char *str, int type)
2613 {
2614   char *p = buf;
2615   int multiline_p = (strchr(str, '\n') != 0);
2616 
2617   if (multiline_p) {
2618     sprintf(p, "\\shortstack[%s]{", just_str_from_text_type(type));
2619     p += strlen(p);
2620   }
2621   else
2622     p[0] = '\0';
2623 
2624   while (*str) {
2625     if (p - buf >= buf_len - (int)(sizeof longest_translation)) {
2626       fprintf(stderr, "string too long; truncated ...%s\n", str);
2627       break;
2628     }
2629     translate_tex_specials(p, *str);
2630     p += strlen(p);
2631     str++;
2632   }
2633 
2634   if (multiline_p) {
2635     strcat(p, "}");
2636     p += strlen(p);
2637   }
2638 
2639   return buf;
2640 }
2641 
2642 /* could make this very fancy; just a brace balance check here */
2643 static void
check_tex_format(char * buf)2644 check_tex_format(char *buf)
2645 {
2646   char *p = buf;
2647   int ch, n_open_braces, n_dollars;
2648 
2649   n_open_braces = n_dollars = 0;
2650   while ((ch = *p++) != '\0') {
2651     switch (ch) {
2652     case '{':
2653       n_open_braces++;
2654       break;
2655     case '}':
2656       n_open_braces--;
2657       break;
2658     case '\\':
2659       if (*p != '\0')
2660 	ch = *p++;
2661       break;
2662     case '$':
2663       n_dollars++;
2664       break;
2665     }
2666   }
2667   if (n_open_braces != 0)
2668     fprintf(stderr, "warning: unbalanced braces in special text '%s'\n", buf);
2669   if ((n_dollars & 1) != 0)
2670     fprintf(stderr, "warning: unbalanced $'s in special text '%s'\n", buf);
2671 }
2672 
2673 /* copied from psfonts */
2674 static int PSfontmap[] = {
2675   ROMAN_FONT,ROMAN_FONT,/* Times-Roman */
2676   ITALIC_FONT,		/* Times-Italic */
2677   BOLD_FONT,		/* Times-Bold */
2678   BOLD_FONT,		/* Times-BoldItalic */
2679   ROMAN_FONT,		/* AvantGarde */
2680   ROMAN_FONT,		/* AvantGarde-BookOblique */
2681   ROMAN_FONT,		/* AvantGarde-Demi */
2682   ROMAN_FONT,		/* AvantGarde-DemiOblique */
2683   ROMAN_FONT,		/* Bookman-Light */
2684   ITALIC_FONT,		/* Bookman-LightItalic */
2685   ROMAN_FONT,		/* Bookman-Demi */
2686   ITALIC_FONT,		/* Bookman-DemiItalic */
2687   TYPEWRITER_FONT,	/* Courier */
2688   TYPEWRITER_FONT,	/* Courier-Oblique */
2689   BOLD_FONT,		/* Courier-Bold */
2690   BOLD_FONT,		/* Courier-BoldItalic */
2691   MODERN_FONT,		/* Helvetica */
2692   MODERN_FONT,		/* Helvetica-Oblique */
2693   BOLD_FONT,		/* Helvetica-Bold */
2694   BOLD_FONT,		/* Helvetica-BoldOblique */
2695   MODERN_FONT,		/* Helvetica-Narrow */
2696   MODERN_FONT,		/* Helvetica-Narrow-Oblique */
2697   BOLD_FONT,		/* Helvetica-Narrow-Bold */
2698   BOLD_FONT,		/* Helvetica-Narrow-BoldOblique */
2699   ROMAN_FONT,		/* NewCenturySchlbk-Roman */
2700   ITALIC_FONT,		/* NewCenturySchlbk-Italic */
2701   BOLD_FONT,		/* NewCenturySchlbk-Bold */
2702   BOLD_FONT,		/* NewCenturySchlbk-BoldItalic */
2703   ROMAN_FONT,		/* Palatino-Roman */
2704   ITALIC_FONT,		/* Palatino-Italic */
2705   BOLD_FONT,		/* Palatino-Bold */
2706   BOLD_FONT,		/* Palatino-BoldItalic */
2707   ROMAN_FONT,		/* Symbol */
2708   ROMAN_FONT,		/* ZapfChancery-MediumItalic */
2709   ROMAN_FONT		/* ZapfDingbats */
2710 };
2711 
2712 static int PSmapwarn[] = {
2713   False, False,		/* Times-Roman */
2714   False,		/* Times-Italic */
2715   False,		/* Times-Bold */
2716   False,		/* Times-BoldItalic */
2717   True,			/* AvantGarde */
2718   True,			/* AvantGarde-BookOblique */
2719   True,			/* AvantGarde-Demi */
2720   True,			/* AvantGarde-DemiOblique */
2721   True,			/* Bookman-Light */
2722   True,			/* Bookman-LightItalic */
2723   True,			/* Bookman-Demi */
2724   True,			/* Bookman-DemiItalic */
2725   False,		/* Courier */
2726   True,			/* Courier-Oblique */
2727   True,			/* Courier-Bold */
2728   True,			/* Courier-BoldItalic */
2729   False,		/* Helvetica */
2730   True,			/* Helvetica-Oblique */
2731   True,			/* Helvetica-Bold */
2732   True,			/* Helvetica-BoldOblique */
2733   True,			/* Helvetica-Narrow */
2734   True,			/* Helvetica-Narrow-Oblique */
2735   True,			/* Helvetica-Narrow-Bold */
2736   True,			/* Helvetica-Narrow-BoldOblique */
2737   True,			/* NewCenturySchlbk-Roman */
2738   True,			/* NewCenturySchlbk-Italic */
2739   True,			/* NewCenturySchlbk-Bold */
2740   True,			/* NewCenturySchlbk-BoldItalic */
2741   True,			/* Palatino-Roman */
2742   True,			/* Palatino-Italic */
2743   True,			/* Palatino-Bold */
2744   True,			/* Palatino-BoldItalic */
2745   True,			/* Symbol */
2746   True,			/* ZapfChancery-MediumItalic */
2747   True			/* ZapfDingbats */
2748 };
2749 
2750 static char *figfontnames[] = {
2751   "Roman", "Roman",
2752   "Roman",
2753   "Bold",
2754   "Italic",
2755   "Modern",
2756   "Typewriter"
2757 };
2758 
2759 
2760 /* taken from the epic driver */
2761 static void
begin_set_font(text)2762 begin_set_font(text)
2763      F_text *text;
2764 {
2765   int texsize;
2766   double baselineskip;
2767 
2768   /* substitute latex for postscript fonts and warn the user (from psfont) */
2769   if (psfont_text(text)) {
2770     if (PSmapwarn[text->font+1]) {
2771       warn(W_PS_FONT);
2772       if (Verbose)
2773         fprintf(stderr, "Substituting LaTeX %s for Postscript %s ('%s')\n",
2774 		figfontnames[PSfontmap[text->font+1]+1], PSfontnames[text->font+1],
2775 		text->cstring);
2776     }
2777     if (text->font == -1) /* leave default to be default, but no-ps */
2778       text->font = 0;
2779     else
2780       text->font = PSfontmap[text->font+1];
2781   }
2782   texsize = TEXFONTMAG(text);
2783   baselineskip = (texsize * 1.2);
2784 
2785 #ifdef NFSS
2786   if (Font_handling == FH_SIZE_ONLY)
2787     fprintf(tfp,
2788 	    "\\begingroup\\SetFigFontSizeOnly{%d}{%.1f}%%\n",
2789 	    texsize, baselineskip);
2790   else if (Font_handling == FH_FULL)
2791     fprintf(tfp,
2792 	    "\\begingroup\\SetFigFont{%d}{%.1f}{%s}{%s}{%s}%%\n",
2793 	    texsize, baselineskip,
2794 	    TEXFAMILY(text->font),TEXSERIES(text->font),
2795 	    TEXSHAPE(text->font));
2796 #else
2797   fprintf(tfp, "\\begingroup\\SetFigFont{%d}{%.1f}{%s}%%\n",
2798 	  texsize, baselineskip, TEXFONT(text->font));
2799 #endif
2800 }
2801 
2802 static void
end_set_font(text)2803 end_set_font(text)
2804      F_text *text;
2805 {
2806   (void)text; /* avoid unused parameter warnings */
2807   if (Font_handling == FH_SIZE_ONLY || Font_handling == FH_FULL)
2808     fprintf(tfp, "\\endgroup%%\n");
2809 }
2810 
2811 void
genpstrx_text(text)2812 genpstrx_text(text)
2813      F_text *text;
2814 {
2815   int x, y;
2816   char formatted_text[64*1024];
2817   char angle[32];
2818 
2819   /* print any comments prefixed with "%" */
2820   print_comments("% ",text->comments, "");
2821 
2822   if (Verbose)
2823     fprintf(tfp, "%% text - %s\n", special_text(text) ? "special" : "no flags");
2824 
2825   begin_set_font(text);
2826 
2827   convert_xy_to_image_bb(&x, &y, text->base_x, text->base_y);
2828 
2829   if (text->angle == 0)
2830     angle[0] = '\0';
2831   else
2832     sprintf(angle, "{%.2lf}", text->angle * (180.0 / M_PI));
2833 
2834   if (special_text(text)) {
2835     check_tex_format(text->cstring);
2836     fprintf(tfp, "\\rput[%s]%s("DBL","DBL"){%s}%%\n",
2837 	    ref_pt_str_from_text_type(text->type),
2838 	    angle,
2839 	    cm_from_1200ths(x), cm_from_1200ths(y),
2840 	    text->cstring);
2841   }
2842   else {
2843     fprintf(tfp, "\\rput[%s]%s("DBL","DBL"){%s}%%\n",
2844 	    ref_pt_str_from_text_type(text->type),
2845 	    angle,
2846 	    cm_from_1200ths(x), cm_from_1200ths(y),
2847 	    fmt_text_str(formatted_text, sizeof formatted_text,
2848 			 text->cstring,
2849 			 text->type));
2850   }
2851   end_set_font(text);
2852 }
2853 
2854 void
genpstrx_arc(arc)2855 genpstrx_arc(arc)
2856      F_arc *arc;
2857 {
2858   int x_0, y_0, x_1, y_1;
2859   double x, y, t_0, t_1, r;
2860   char options[256];
2861   F_arrow *a_0, *a_1;
2862 
2863   /* print any comments prefixed with "%" */
2864   print_comments("% ",arc->comments,"");
2865 
2866   if (Verbose)
2867     fprintf(tfp, "%% arc\n");
2868 
2869   convert_xy_dbl_to_image_bb(&x, &y, arc->center.x, arc->center.y);
2870 
2871   if (arc->direction == 1) {
2872     convert_xy_to_image_bb(&x_0, &y_0, arc->point[0].x, arc->point[0].y);
2873     convert_xy_to_image_bb(&x_1, &y_1, arc->point[2].x, arc->point[2].y);
2874   }
2875   else {
2876     convert_xy_to_image_bb(&x_0, &y_0, arc->point[2].x, arc->point[2].y);
2877     convert_xy_to_image_bb(&x_1, &y_1, arc->point[0].x, arc->point[0].y);
2878   }
2879   t_0 = atan2(y_0 - y, x_0 - x) * (180.0 / PI);
2880   t_1 = atan2(y_1 - y, x_1 - x) * (180.0 / PI);
2881   r = sqrt(sqr_dbl((double)(x_0 - x)) + sqr_dbl((double)(y_0 - y)));
2882 
2883 
2884   switch (arc->type) {
2885 
2886   case T_OPEN_ARC:
2887 
2888     if (arc->direction == 1) {
2889       a_0 = arc->back_arrow;
2890       a_1 = arc->for_arrow;
2891     }
2892     else {
2893       a_0 = arc->for_arrow;
2894       a_1 = arc->back_arrow;
2895     }
2896 
2897 #define FO_ARC_STD (FO_LINE_STYLE | FO_PEN_COLOR | FO_LINE_TERM | FO_FILL)
2898     format_options(options, 0, 0, 0,
2899 		   Arrows == A_XFIG     ?  FO_ARC_STD :
2900 		   Arrows == A_PSTRICKS ? (FO_ARC_STD | FO_ARROWS | FO_ARROW_SIZE) :
2901 		   /* default */          (FO_ARC_STD | FO_ARROWS),
2902 		   arc->thickness, arc->style, arc->style_val,
2903 		   arc->pen_color, 0, arc->cap_style, arc->fill_style, arc->fill_color,
2904 		   a_0, a_1, 0.0);
2905 
2906     fprintf(tfp, "\\psarc%s("DBL","DBL"){"DBL"}{"DBL"}{"DBL"}%%\n",
2907 	    options,
2908 	    cm_from_1200ths(x), cm_from_1200ths(y),
2909 	    cm_from_1200ths(r),
2910 	    t_0, t_1);
2911     if (Arrows == A_XFIG) {
2912       if (a_0)
2913 	put_arrowhead((double)x_0, (double)y_0,
2914 		      (double)(y_0 - y), (double)(x - x_0),
2915 		      a_0->wid, a_0->ht, max_dbl(a_0->thickness, (double)arc->thickness),
2916 		      a_0->type, a_0->style,
2917 		      arc->pen_color);
2918       if (a_1)
2919 	put_arrowhead((double)x_1, (double)y_1,
2920 		      (double)(y - y_1), (double)(x_1 - x), /* perp of radius vector */
2921 		      a_1->wid, a_1->ht, max_dbl(a_1->thickness, (double)arc->thickness),
2922 		      a_1->type, a_1->style,
2923 		      arc->pen_color);
2924     }
2925     break;
2926 
2927   case T_PIE_WEDGE_ARC:
2928     format_options(options, 0, 0, 0,
2929 		   FO_LINE_STYLE | FO_PEN_COLOR | FO_FILL,
2930 		   arc->thickness, arc->style, arc->style_val,
2931 		   arc->pen_color, 0, arc->cap_style, arc->fill_style, arc->fill_color,
2932 		   0,0,0.0);
2933 
2934     fprintf(tfp, "\\pswedge%s("DBL","DBL"){"DBL"}{"DBL"}{"DBL"}%%\n",
2935 	    options,
2936 	    cm_from_1200ths(x), cm_from_1200ths(y),
2937 	    cm_from_1200ths(r),
2938 	    t_0, t_1);
2939     break;
2940   default:
2941     fprintf(stderr, "unknown arc type (%d)\n", arc->type);
2942   }
2943 }
2944 
genpstrx_grid(major,minor)2945 void genpstrx_grid(major, minor)
2946      double major, minor;
2947 {
2948   if (minor == 0.0 && major == 0.0)
2949     return;
2950   fprintf(tfp, "\\psgrid[gridcolor=lightgray,subgridcolor=lightgray]\n");
2951 }
2952 
2953 struct driver dev_pstricks = {
2954   genpstrx_option,
2955   genpstrx_start,
2956   genpstrx_grid,
2957   genpstrx_arc,
2958   genpstrx_ellipse,
2959   genpstrx_line,
2960   genpstrx_spline,
2961   genpstrx_text,
2962   genpstrx_end,
2963   INCLUDE_TEXT
2964 };
2965 
2966