Move HID drawing API prototypes into a separate header file
[geda-pcb/pcjc2.git] / src / hid / ps / eps.c
blob144549170a9d3f9d4e491c2008bd8c5349044959
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
11 #include "global.h"
12 #include "data.h"
13 #include "misc.h"
14 #include "pcb-printf.h"
16 #include "hid.h"
17 #include "hid_draw.h"
18 #include "../hidint.h"
19 #include "hid/common/hidnogui.h"
20 #include "hid/common/draw_helpers.h"
21 #include "../ps/ps.h"
22 #include "hid/common/hidinit.h"
24 #ifdef HAVE_LIBDMALLOC
25 #include <dmalloc.h>
26 #endif
28 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented EPS function %s.\n", __FUNCTION__); abort()
30 /*----------------------------------------------------------------------------*/
31 /* Function prototypes */
32 /*----------------------------------------------------------------------------*/
33 static HID_Attribute * eps_get_export_options (int *n);
34 static void eps_do_export (HID_Attr_Val * options);
35 static void eps_parse_arguments (int *argc, char ***argv);
36 static int eps_set_layer (const char *name, int group, int empty);
37 static hidGC eps_make_gc (void);
38 static void eps_destroy_gc (hidGC gc);
39 static void eps_use_mask (enum mask_mode mode);
40 static void eps_set_color (hidGC gc, const char *name);
41 static void eps_set_line_cap (hidGC gc, EndCapStyle style);
42 static void eps_set_line_width (hidGC gc, Coord width);
43 static void eps_set_draw_xor (hidGC gc, int _xor);
44 static void eps_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
45 static void eps_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
46 static void eps_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle);
47 static void eps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
48 static void eps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
49 static void eps_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y);
50 static void eps_calibrate (double xval, double yval);
51 static void eps_set_crosshair (int x, int y, int action);
52 /*----------------------------------------------------------------------------*/
54 typedef struct hid_gc_struct
56 EndCapStyle cap;
57 Coord width;
58 int color;
59 int erase;
60 } hid_gc_struct;
62 static HID eps_hid;
63 static HID_DRAW eps_graphics;
65 static FILE *f = 0;
66 static Coord linewidth = -1;
67 static int lastcap = -1;
68 static int lastcolor = -1;
69 static int print_group[MAX_LAYER];
70 static int print_layer[MAX_LAYER];
71 static int fast_erase = -1;
73 static HID_Attribute eps_attribute_list[] = {
74 /* other HIDs expect this to be first. */
76 /* %start-doc options "92 Encapsulated Postscript Export"
77 @ftable @code
78 @item --eps-file <string>
79 Name of the encapsulated postscript output file. Can contain a path.
80 @end ftable
81 %end-doc
83 {"eps-file", "Encapsulated Postscript output file",
84 HID_String, 0, 0, {0, 0, 0}, 0, 0},
85 #define HA_psfile 0
87 /* %start-doc options "92 Encapsulated Postscript Export"
88 @ftable @code
89 @item --eps-scale <num>
90 Scale EPS output by the parameter @samp{num}.
91 @end ftable
92 %end-doc
94 {"eps-scale", "EPS scale",
95 HID_Real, 0, 100, {0, 0, 1.0}, 0, 0},
96 #define HA_scale 1
98 /* %start-doc options "92 Encapsulated Postscript Export"
99 @ftable @code
100 @cindex as-shown (EPS)
101 @item --as-shown
102 Export layers as shown on screen.
103 @end ftable
104 %end-doc
106 {"as-shown", "Export layers as shown on screen",
107 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
108 #define HA_as_shown 2
110 /* %start-doc options "92 Encapsulated Postscript Export"
111 @ftable @code
112 @item --monochrome
113 Convert output to monochrome.
114 @end ftable
115 %end-doc
117 {"monochrome", "Convert to monochrome",
118 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
119 #define HA_mono 3
121 /* %start-doc options "92 Encapsulated Postscript Export"
122 @ftable @code
123 @cindex only-visible
124 @item --only-visible
125 Limit the bounds of the EPS file to the visible items.
126 @end ftable
127 %end-doc
129 {"only-visible", "Limit the bounds of the EPS file to the visible items",
130 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
131 #define HA_only_visible 4
134 #define NUM_OPTIONS (sizeof(eps_attribute_list)/sizeof(eps_attribute_list[0]))
136 REGISTER_ATTRIBUTES (eps_attribute_list)
138 static HID_Attr_Val eps_values[NUM_OPTIONS];
140 static HID_Attribute *
141 eps_get_export_options (int *n)
143 static char *last_made_filename = 0;
145 if (PCB) derive_default_filename(PCB->Filename, &eps_attribute_list[HA_psfile], ".eps", &last_made_filename);
147 if (n)
148 *n = NUM_OPTIONS;
149 return eps_attribute_list;
152 static int comp_layer, solder_layer;
154 static int
155 group_for_layer (int l)
157 if (l < max_copper_layer + 2 && l >= 0)
158 return GetLayerGroupNumberByNumber (l);
159 /* else something unique */
160 return max_group + 3 + l;
163 static int
164 layer_sort (const void *va, const void *vb)
166 int a = *(int *) va;
167 int b = *(int *) vb;
168 int al = group_for_layer (a);
169 int bl = group_for_layer (b);
170 int d = bl - al;
172 if (a >= 0 && a <= max_copper_layer + 1)
174 int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1);
175 int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1);
176 if (bside != aside)
177 return bside - aside;
179 if (d)
180 return d;
181 return b - a;
184 static const char *filename;
185 static BoxType *bounds;
186 static int in_mono, as_shown;
188 void
189 eps_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
191 int i;
192 static int saved_layer_stack[MAX_LAYER];
193 BoxType region;
194 FlagType save_thindraw;
196 save_thindraw = PCB->Flags;
197 CLEAR_FLAG(THINDRAWFLAG, PCB);
198 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
199 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
201 f = the_file;
203 region.X1 = 0;
204 region.Y1 = 0;
205 region.X2 = PCB->MaxWidth;
206 region.Y2 = PCB->MaxHeight;
208 if (options[HA_only_visible].int_value)
209 bounds = GetDataBoundingBox (PCB->Data);
210 else
211 bounds = &region;
213 memset (print_group, 0, sizeof (print_group));
214 memset (print_layer, 0, sizeof (print_layer));
216 /* Figure out which layers actually have stuff on them. */
217 for (i = 0; i < max_copper_layer; i++)
219 LayerType *layer = PCB->Data->Layer + i;
220 if (layer->On)
221 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
222 print_group[GetLayerGroupNumberByNumber (i)] = 1;
225 /* Now, if only one layer has real stuff on it, we can use the fast
226 erase logic. Otherwise, we have to use the expensive multi-mask
227 erase. */
228 fast_erase = 0;
229 for (i = 0; i < max_group; i++)
230 if (print_group[i])
231 fast_erase ++;
233 /* If NO layers had anything on them, at least print the component
234 layer to get the pins. */
235 if (fast_erase == 0)
237 print_group[GetLayerGroupNumberByNumber (component_silk_layer)] = 1;
238 fast_erase = 1;
241 /* "fast_erase" is 1 if we can just paint white to erase. */
242 fast_erase = fast_erase == 1 ? 1 : 0;
244 /* Now, for each group we're printing, mark its layers for
245 printing. */
246 for (i = 0; i < max_copper_layer; i++)
247 if (print_group[GetLayerGroupNumberByNumber (i)])
248 print_layer[i] = 1;
250 if (fast_erase) {
251 eps_hid.poly_before = 1;
252 eps_hid.poly_after = 0;
253 } else {
254 eps_hid.poly_before = 0;
255 eps_hid.poly_after = 1;
258 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
259 as_shown = options[HA_as_shown].int_value;
260 if (!options[HA_as_shown].int_value)
262 comp_layer = GetLayerGroupNumberByNumber (component_silk_layer);
263 solder_layer = GetLayerGroupNumberByNumber (solder_silk_layer);
264 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_sort);
266 fprintf (f, "%%!PS-Adobe-3.0 EPSF-3.0\n");
267 linewidth = -1;
268 lastcap = -1;
269 lastcolor = -1;
271 in_mono = options[HA_mono].int_value;
273 #define pcb2em(x) 1 + COORD_TO_INCH (x) * 72.0 * options[HA_scale].real_value
274 fprintf (f, "%%%%BoundingBox: 0 0 %lli %lli\n",
275 llrint (pcb2em (bounds->X2 - bounds->X1)),
276 llrint (pcb2em (bounds->Y2 - bounds->Y1)) );
277 fprintf (f, "%%%%HiResBoundingBox: 0.000000 0.000000 %.6f %.6f\n",
278 pcb2em (bounds->X2 - bounds->X1),
279 pcb2em (bounds->Y2 - bounds->Y1));
280 #undef pcb2em
281 fprintf (f, "%%%%Pages: 1\n");
282 fprintf (f,
283 "save countdictstack mark newpath /showpage {} def /setpagedevice {pop} def\n");
284 fprintf (f, "%%%%EndProlog\n");
285 fprintf (f, "%%%%Page: 1 1\n");
286 fprintf (f, "%%%%BeginDocument: %s\n\n", filename);
288 fprintf (f, "72 72 scale\n");
289 fprintf (f, "1 dup neg scale\n");
290 fprintf (f, "%g dup scale\n", options[HA_scale].real_value);
291 pcb_fprintf (f, "%mi %mi translate\n", -bounds->X1, -bounds->Y2);
292 if (options[HA_as_shown].int_value && Settings.ShowSolderSide)
293 pcb_fprintf (f, "-1 1 scale %mi 0 translate\n", bounds->X1 - bounds->X2);
294 linewidth = -1;
295 lastcap = -1;
296 lastcolor = -1;
297 #define Q (Coord) MIL_TO_COORD(10)
298 pcb_fprintf (f,
299 "/nclip { %mi %mi moveto %mi %mi lineto %mi %mi lineto %mi %mi lineto %mi %mi lineto eoclip newpath } def\n",
300 bounds->X1 - Q, bounds->Y1 - Q, bounds->X1 - Q, bounds->Y2 + Q,
301 bounds->X2 + Q, bounds->Y2 + Q, bounds->X2 + Q, bounds->Y1 - Q,
302 bounds->X1 - Q, bounds->Y1 - Q);
303 #undef Q
304 fprintf (f, "/t { moveto lineto stroke } bind def\n");
305 fprintf (f, "/tc { moveto lineto strokepath nclip } bind def\n");
306 fprintf (f, "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n");
307 fprintf (f,
308 " x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n");
309 fprintf (f, "/c { 0 360 arc fill } bind def\n");
310 fprintf (f, "/cc { 0 360 arc nclip } bind def\n");
311 fprintf (f,
312 "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
314 hid_expose_callback (&eps_hid, bounds, 0);
316 fprintf (f, "showpage\n");
318 fprintf (f, "%%%%EndDocument\n");
319 fprintf (f, "%%%%Trailer\n");
320 fprintf (f, "cleartomark countdictstack exch sub { end } repeat restore\n");
321 fprintf (f, "%%%%EOF\n");
323 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
324 PCB->Flags = save_thindraw;
327 static void
328 eps_do_export (HID_Attr_Val * options)
330 int i;
331 int save_ons[MAX_LAYER + 2];
333 if (!options)
335 eps_get_export_options (0);
336 for (i = 0; i < NUM_OPTIONS; i++)
337 eps_values[i] = eps_attribute_list[i].default_val;
338 options = eps_values;
341 filename = options[HA_psfile].str_value;
342 if (!filename)
343 filename = "pcb-out.eps";
345 f = fopen (filename, "w");
346 if (!f)
348 perror (filename);
349 return;
352 if (!options[HA_as_shown].int_value)
353 hid_save_and_show_layer_ons (save_ons);
354 eps_hid_export_to_file (f, options);
355 if (!options[HA_as_shown].int_value)
356 hid_restore_layer_ons (save_ons);
358 fclose (f);
361 static void
362 eps_parse_arguments (int *argc, char ***argv)
364 hid_register_attributes (eps_attribute_list,
365 sizeof (eps_attribute_list) /
366 sizeof (eps_attribute_list[0]));
367 hid_parse_command_line (argc, argv);
370 static int is_mask;
371 static int is_paste;
372 static int is_drill;
374 static int
375 eps_set_layer (const char *name, int group, int empty)
377 int idx = (group >= 0
378 && group <
379 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
380 if (name == 0)
381 name = PCB->Data->Layer[idx].Name;
383 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
384 return 0;
385 if (SL_TYPE (idx) == SL_ASSY || SL_TYPE (idx) == SL_FAB)
386 return 0;
388 if (strcmp (name, "invisible") == 0)
389 return 0;
391 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
392 is_mask = (SL_TYPE (idx) == SL_MASK);
393 is_paste = (SL_TYPE (idx) == SL_PASTE);
395 if (is_mask || is_paste)
396 return 0;
397 #if 0
398 printf ("Layer %s group %d drill %d mask %d\n", name, group, is_drill,
399 is_mask);
400 #endif
401 fprintf (f, "%% Layer %s group %d drill %d mask %d\n", name, group,
402 is_drill, is_mask);
404 if (as_shown)
406 switch (idx)
408 case SL (SILK, TOP):
409 case SL (SILK, BOTTOM):
410 if (SL_MYSIDE (idx))
411 return PCB->ElementOn;
412 else
413 return 0;
416 else
418 switch (idx)
420 case SL (SILK, TOP):
421 return 1;
422 case SL (SILK, BOTTOM):
423 return 0;
427 return 1;
430 static hidGC
431 eps_make_gc (void)
433 hidGC rv = (hidGC) malloc (sizeof (hid_gc_struct));
434 rv->cap = Trace_Cap;
435 rv->width = 0;
436 rv->color = 0;
437 return rv;
440 static void
441 eps_destroy_gc (hidGC gc)
443 free (gc);
446 static void
447 eps_use_mask (enum mask_mode mode)
449 static int mask_pending = 0;
450 switch (mode)
452 case HID_MASK_CLEAR:
453 if (!mask_pending)
455 mask_pending = 1;
456 fprintf (f, "gsave\n");
458 break;
459 case HID_MASK_BEFORE:
460 case HID_MASK_AFTER:
461 break;
462 case HID_MASK_OFF:
463 if (mask_pending)
465 mask_pending = 0;
466 fprintf (f, "grestore\n");
467 lastcolor = -1;
469 break;
473 static void
474 eps_set_color (hidGC gc, const char *name)
476 static void *cache = 0;
477 hidval cval;
479 if (strcmp (name, "erase") == 0)
481 gc->color = 0xffffff;
482 gc->erase = fast_erase ? 0 : 1;
483 return;
485 if (strcmp (name, "drill") == 0)
487 gc->color = 0xffffff;
488 gc->erase = 0;
489 return;
491 gc->erase = 0;
492 if (hid_cache_color (0, name, &cval, &cache))
494 gc->color = cval.lval;
496 else if (in_mono)
498 gc->color = 0;
500 else if (name[0] == '#')
502 unsigned int r, g, b;
503 sscanf (name + 1, "%2x%2x%2x", &r, &g, &b);
504 gc->color = (r << 16) + (g << 8) + b;
506 else
507 gc->color = 0;
510 static void
511 eps_set_line_cap (hidGC gc, EndCapStyle style)
513 gc->cap = style;
516 static void
517 eps_set_line_width (hidGC gc, Coord width)
519 gc->width = width;
522 static void
523 eps_set_draw_xor (hidGC gc, int xor_)
528 static void
529 use_gc (hidGC gc)
531 if (linewidth != gc->width)
533 pcb_fprintf (f, "%mi setlinewidth\n", gc->width);
534 linewidth = gc->width;
536 if (lastcap != gc->cap)
538 int c;
539 switch (gc->cap)
541 case Round_Cap:
542 case Trace_Cap:
543 c = 1;
544 break;
545 default:
546 case Square_Cap:
547 c = 2;
548 break;
550 fprintf (f, "%d setlinecap\n", c);
551 lastcap = gc->cap;
553 if (lastcolor != gc->color)
555 int c = gc->color;
556 #define CV(x,b) (((x>>b)&0xff)/255.0)
557 fprintf (f, "%g %g %g setrgbcolor\n", CV (c, 16), CV (c, 8), CV (c, 0));
558 lastcolor = gc->color;
562 static void eps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
563 static void eps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
565 static void
566 eps_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
568 use_gc (gc);
569 pcb_fprintf (f, "%mi %mi %mi %mi r\n", x1, y1, x2, y2);
572 static void
573 eps_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
575 Coord w = gc->width / 2;
576 if (x1 == x2 && y1 == y2)
578 if (gc->cap == Square_Cap)
579 eps_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
580 else
581 eps_fill_circle (gc, x1, y1, w);
582 return;
584 use_gc (gc);
585 if (gc->erase && gc->cap != Square_Cap)
587 double ang = atan2 (y2 - y1, x2 - x1);
588 double dx = w * sin (ang);
589 double dy = -w * cos (ang);
590 double deg = ang * 180.0 / M_PI;
591 Coord vx1 = x1 + dx;
592 Coord vy1 = y1 + dy;
594 pcb_fprintf (f, "%mi %mi moveto ", vx1, vy1);
595 pcb_fprintf (f, "%mi %mi %mi %g %g arc\n", x2, y2, w, deg - 90, deg + 90);
596 pcb_fprintf (f, "%mi %mi %mi %g %g arc\n", x1, y1, w, deg + 90, deg + 270);
597 fprintf (f, "nclip\n");
599 return;
601 pcb_fprintf (f, "%mi %mi %mi %mi %s\n", x1, y1, x2, y2, gc->erase ? "tc" : "t");
604 static void
605 eps_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
606 Angle start_angle, Angle delta_angle)
608 Angle sa, ea;
609 if (delta_angle > 0)
611 sa = start_angle;
612 ea = start_angle + delta_angle;
614 else
616 sa = start_angle + delta_angle;
617 ea = start_angle;
619 #if 0
620 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
621 cx, cy, width, height, start_angle, delta_angle, sa, ea);
622 #endif
623 use_gc (gc);
624 pcb_fprintf (f, "%ma %ma %mi %mi %mi %mi %g a\n",
625 sa, ea, -width, height, cx, cy, (double) linewidth / width);
628 static void
629 eps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
631 use_gc (gc);
632 pcb_fprintf (f, "%mi %mi %mi %s\n", cx, cy, radius, gc->erase ? "cc" : "c");
635 static void
636 eps_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
638 int i;
639 char *op = "moveto";
640 use_gc (gc);
641 for (i = 0; i < n_coords; i++)
643 pcb_fprintf (f, "%mi %mi %s\n", x[i], y[i], op);
644 op = "lineto";
646 fprintf (f, "fill\n");
649 static void
650 eps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
652 use_gc (gc);
653 pcb_fprintf (f, "%mi %mi %mi %mi r\n", x1, y1, x2, y2);
656 static void
657 eps_calibrate (double xval, double yval)
659 CRASH;
662 static void
663 eps_set_crosshair (int x, int y, int action)
667 void
668 hid_eps_init ()
670 memset (&eps_hid, 0, sizeof (HID));
671 memset (&eps_graphics, 0, sizeof (HID_DRAW));
673 common_nogui_init (&eps_hid);
674 common_draw_helpers_init (&eps_graphics);
676 eps_hid.struct_size = sizeof (HID);
677 eps_hid.name = "eps";
678 eps_hid.description = "Encapsulated Postscript";
679 eps_hid.exporter = 1;
680 eps_hid.poly_after = 1;
682 eps_hid.get_export_options = eps_get_export_options;
683 eps_hid.do_export = eps_do_export;
684 eps_hid.parse_arguments = eps_parse_arguments;
685 eps_hid.set_layer = eps_set_layer;
686 eps_hid.calibrate = eps_calibrate;
687 eps_hid.set_crosshair = eps_set_crosshair;
689 eps_hid.graphics = &eps_graphics;
691 eps_graphics.make_gc = eps_make_gc;
692 eps_graphics.destroy_gc = eps_destroy_gc;
693 eps_graphics.use_mask = eps_use_mask;
694 eps_graphics.set_color = eps_set_color;
695 eps_graphics.set_line_cap = eps_set_line_cap;
696 eps_graphics.set_line_width = eps_set_line_width;
697 eps_graphics.set_draw_xor = eps_set_draw_xor;
698 eps_graphics.draw_line = eps_draw_line;
699 eps_graphics.draw_arc = eps_draw_arc;
700 eps_graphics.draw_rect = eps_draw_rect;
701 eps_graphics.fill_circle = eps_fill_circle;
702 eps_graphics.fill_polygon = eps_fill_polygon;
703 eps_graphics.fill_rect = eps_fill_rect;
705 hid_register_hid (&eps_hid);