Move some fields from the HID* structure to HID_DRAW* and HID_DRAW_CLASS*
[geda-pcb/pcjc2.git] / src / hid / ps / ps.c
blob1572ccc8c08343d8732441ca3a3383a1d87a0247
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
6 #include <stdarg.h> /* not used */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h> /* not used */
10 #include <time.h>
12 #include "global.h"
13 #include "data.h"
14 #include "misc.h"
15 #include "error.h"
16 #include "draw.h"
17 #include "pcb-printf.h"
19 #include "hid.h"
20 #include "hid_draw.h"
21 #include "../hidint.h"
22 #include "hid/common/hidnogui.h"
23 #include "hid/common/draw_helpers.h"
24 #include "../ps/ps.h"
25 #include "../../print.h"
26 #include "hid/common/hidinit.h"
28 #ifdef HAVE_LIBDMALLOC
29 #include <dmalloc.h>
30 #endif
32 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PS function %s.\n", __FUNCTION__); abort()
34 static int ps_set_layer (const char *name, int group, int empty);
35 static void use_gc (hidGC gc);
37 typedef struct ps_gc_struct
39 struct hid_gc_struct hid_gc; /* Parent */
41 EndCapStyle cap;
42 Coord width;
43 unsigned char r, g, b;
44 int erase;
45 int faded;
46 } *psGC;
48 HID ps_hid;
49 static HID_DRAW ps_graphics;
50 static HID_DRAW_CLASS ps_graphics_class;
52 static const char *medias[] = {
53 "A0", "A1", "A2", "A3", "A4", "A5",
54 "A6", "A7", "A8", "A9", "A10",
55 "B0", "B1", "B2", "B3", "B4", "B5",
56 "B6", "B7", "B8", "B9", "B10",
57 "Letter", "11x17", "Ledger",
58 "Legal", "Executive",
59 "A-Size", "B-size",
60 "C-Size", "D-size", "E-size",
61 "US-Business_Card", "Intl-Business_Card",
65 typedef struct
67 char *name;
68 Coord Width, Height;
69 Coord MarginX, MarginY;
70 } MediaType;
73 * Metric ISO sizes in mm. See http://en.wikipedia.org/wiki/ISO_paper_sizes
75 * A0 841 x 1189
76 * A1 594 x 841
77 * A2 420 x 594
78 * A3 297 x 420
79 * A4 210 x 297
80 * A5 148 x 210
81 * A6 105 x 148
82 * A7 74 x 105
83 * A8 52 x 74
84 * A9 37 x 52
85 * A10 26 x 37
87 * B0 1000 x 1414
88 * B1 707 x 1000
89 * B2 500 x 707
90 * B3 353 x 500
91 * B4 250 x 353
92 * B5 176 x 250
93 * B6 125 x 176
94 * B7 88 x 125
95 * B8 62 x 88
96 * B9 44 x 62
97 * B10 31 x 44
99 * awk '{printf(" {\"%s\", %d, %d, MARGINX, MARGINY},\n", $2, $3*100000/25.4, $5*100000/25.4)}'
101 * See http://en.wikipedia.org/wiki/Paper_size#Loose_sizes for some of the other sizes. The
102 * {A,B,C,D,E}-Size here are the ANSI sizes and not the architectural sizes.
105 #define MARGINX MIL_TO_COORD(500)
106 #define MARGINY MIL_TO_COORD(500)
108 static MediaType media_data[] = {
109 {"A0", MM_TO_COORD(841), MM_TO_COORD(1189), MARGINX, MARGINY},
110 {"A1", MM_TO_COORD(594), MM_TO_COORD(841), MARGINX, MARGINY},
111 {"A2", MM_TO_COORD(420), MM_TO_COORD(594), MARGINX, MARGINY},
112 {"A3", MM_TO_COORD(297), MM_TO_COORD(420), MARGINX, MARGINY},
113 {"A4", MM_TO_COORD(210), MM_TO_COORD(297), MARGINX, MARGINY},
114 {"A5", MM_TO_COORD(148), MM_TO_COORD(210), MARGINX, MARGINY},
115 {"A6", MM_TO_COORD(105), MM_TO_COORD(148), MARGINX, MARGINY},
116 {"A7", MM_TO_COORD(74), MM_TO_COORD(105), MARGINX, MARGINY},
117 {"A8", MM_TO_COORD(52), MM_TO_COORD(74), MARGINX, MARGINY},
118 {"A9", MM_TO_COORD(37), MM_TO_COORD(52), MARGINX, MARGINY},
119 {"A10", MM_TO_COORD(26), MM_TO_COORD(37), MARGINX, MARGINY},
120 {"B0", MM_TO_COORD(1000), MM_TO_COORD(1414), MARGINX, MARGINY},
121 {"B1", MM_TO_COORD(707), MM_TO_COORD(1000), MARGINX, MARGINY},
122 {"B2", MM_TO_COORD(500), MM_TO_COORD(707), MARGINX, MARGINY},
123 {"B3", MM_TO_COORD(353), MM_TO_COORD(500), MARGINX, MARGINY},
124 {"B4", MM_TO_COORD(250), MM_TO_COORD(353), MARGINX, MARGINY},
125 {"B5", MM_TO_COORD(176), MM_TO_COORD(250), MARGINX, MARGINY},
126 {"B6", MM_TO_COORD(125), MM_TO_COORD(176), MARGINX, MARGINY},
127 {"B7", MM_TO_COORD(88), MM_TO_COORD(125), MARGINX, MARGINY},
128 {"B8", MM_TO_COORD(62), MM_TO_COORD(88), MARGINX, MARGINY},
129 {"B9", MM_TO_COORD(44), MM_TO_COORD(62), MARGINX, MARGINY},
130 {"B10", MM_TO_COORD(31), MM_TO_COORD(44), MARGINX, MARGINY},
131 {"Letter", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
132 {"11x17", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
133 {"Ledger", INCH_TO_COORD(17), INCH_TO_COORD(11), MARGINX, MARGINY},
134 {"Legal", INCH_TO_COORD(8.5), INCH_TO_COORD(14), MARGINX, MARGINY},
135 {"Executive", INCH_TO_COORD(7.5), INCH_TO_COORD(10), MARGINX, MARGINY},
136 {"A-size", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
137 {"B-size", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
138 {"C-size", INCH_TO_COORD(17), INCH_TO_COORD(22), MARGINX, MARGINY},
139 {"D-size", INCH_TO_COORD(22), INCH_TO_COORD(34), MARGINX, MARGINY},
140 {"E-size", INCH_TO_COORD(34), INCH_TO_COORD(44), MARGINX, MARGINY},
141 {"US-Business_Card", INCH_TO_COORD(3.5), INCH_TO_COORD(2.0), 0, 0},
142 {"Intl-Business_Card", INCH_TO_COORD(3.375), INCH_TO_COORD(2.125), 0, 0}
145 #undef MARGINX
146 #undef MARGINY
148 HID_Attribute ps_attribute_list[] = {
149 /* other HIDs expect this to be first. */
151 /* %start-doc options "91 Postscript Export"
152 @ftable @code
153 @item --psfile <string>
154 Name of the postscript output file. Can contain a path.
155 @end ftable
156 %end-doc
158 {N_("psfile"), N_("Postscript output file"),
159 HID_String, 0, 0, {0, 0, 0}, 0, 0},
160 #define HA_psfile 0
162 /* %start-doc options "91 Postscript Export"
163 @ftable @code
164 @cindex drill-helper
165 @item --drill-helper
166 Print a centering target in large drill holes.
167 @end ftable
168 %end-doc
170 {N_("drill-helper"), N_("Print a centering target in large drill holes"),
171 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
172 #define HA_drillhelper 1
174 /* %start-doc options "91 Postscript Export"
175 @ftable @code
176 @cindex align-marks
177 @item --align-marks
178 Print alignment marks on each sheet. This is meant to ease alignment during exposure.
179 @end ftable
180 %end-doc
182 {N_("align-marks"), N_("Print alignment marks on each sheet"),
183 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
184 #define HA_alignmarks 2
186 /* %start-doc options "91 Postscript Export"
187 @ftable @code
188 @item --outline
189 Print the contents of the outline layer on each sheet.
190 @end ftable
191 %end-doc
193 {N_("outline"), N_("Print outline on each sheet"),
194 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
195 #define HA_outline 3
196 /* %start-doc options "91 Postscript Export"
197 @ftable @code
198 @item --mirror
199 Print mirror image.
200 @end ftable
201 %end-doc
203 {N_("mirror"), N_("Print mirror image of every page"),
204 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
205 #define HA_mirror 4
207 /* %start-doc options "91 Postscript Export"
208 @ftable @code
209 @item --fill-page
210 Scale output to make the board fit the page.
211 @end ftable
212 %end-doc
214 {N_("fill-page"), N_("Scale board to fill page"),
215 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
216 #define HA_fillpage 5
218 /* %start-doc options "91 Postscript Export"
219 @ftable @code
220 @item --auto-mirror
221 Print mirror image of appropriate layers.
222 @end ftable
223 %end-doc
225 {N_("auto-mirror"), N_("Print mirror image of appropriate layers"),
226 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
227 #define HA_automirror 6
229 /* %start-doc options "91 Postscript Export"
230 @ftable @code
231 @item --ps-color
232 Postscript output in color.
233 @end ftable
234 %end-doc
236 {N_("ps-color"), N_("Prints in color"),
237 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
238 #define HA_color 7
240 /* %start-doc options "91 Postscript Export"
241 @ftable @code
242 @cindex ps-bloat
243 @item --ps-bloat <num>
244 Amount to add to trace/pad/pin edges.
245 @end ftable
246 %end-doc
248 {N_("ps-bloat"), N_("Amount to add to trace/pad/pin edges"),
249 HID_Coord, -MIL_TO_COORD (100), MIL_TO_COORD (100), {0, 0, 0}, 0, 0},
250 #define HA_psbloat 8
252 /* %start-doc options "91 Postscript Export"
253 @ftable @code
254 @cindex ps-invert
255 @item --ps-invert
256 Draw objects as white-on-black.
257 @end ftable
258 %end-doc
260 {N_("ps-invert"), N_("Draw objects as white-on-black"),
261 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
262 #define HA_psinvert 9
264 /* %start-doc options "91 Postscript Export"
265 @ftable @code
266 @item --media <media-name>
267 Size of the media, the postscript is fitted to. The parameter
268 @code{<media-name>} can be any of the standard names for paper size: @samp{A0}
269 to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
270 @samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
271 @samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
272 @samp{Intl-Business_Card}.
273 @end ftable
274 %end-doc
276 {N_("media"), N_("Media type"),
277 HID_Enum, 0, 0, {22, 0, 0}, medias, 0},
278 #define HA_media 10
280 /* %start-doc options "91 Postscript Export"
281 @ftable @code
282 @cindex psfade
283 @item --psfade <num>
284 Fade amount for assembly drawings (0.0=missing, 1.0=solid).
285 @end ftable
286 %end-doc
288 {N_("psfade"),
289 N_("Fade amount for assembly drawings (0.0=missing, 1.0=solid)"),
290 HID_Real, 0, 1, {0, 0, 0.40}, 0, 0},
291 #define HA_psfade 11
293 /* %start-doc options "91 Postscript Export"
294 @ftable @code
295 @item --scale <num>
296 Scale value to compensate for printer sizing errors (1.0 = full scale).
297 @end ftable
298 %end-doc
300 {N_("scale"),
301 N_("Scale value to compensate for printer sizing errors (1.0 = full scale)"),
302 HID_Real, 0.01, 4, {0, 0, 1.00}, 0, 0},
303 #define HA_scale 12
305 /* %start-doc options "91 Postscript Export"
306 @ftable @code
307 @cindex multi-file
308 @item --multi-file
309 Produce multiple files, one per page, instead of a single multi page file.
310 @end ftable
311 %end-doc
313 {N_("multi-file"),
314 N_("Produce multiple files, one per page, instead of a single file"),
315 HID_Boolean, 0, 0, {0, 0, 0.40}, 0, 0},
316 #define HA_multifile 13
318 /* %start-doc options "91 Postscript Export"
319 @ftable @code
320 @item --xcalib <num>
321 Paper width. Used for x-Axis calibration.
322 @end ftable
323 %end-doc
325 {N_("xcalib"), N_("Paper width. Used for x-Axis calibration"),
326 HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
327 #define HA_xcalib 14
329 /* %start-doc options "91 Postscript Export"
330 @ftable @code
331 @item --ycalib <num>
332 Paper height. Used for y-Axis calibration.
333 @end ftable
334 %end-doc
336 {N_("ycalib"), N_("Paper height. Used for y-Axis calibration"),
337 HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
338 #define HA_ycalib 15
340 /* %start-doc options "91 Postscript Export"
341 @ftable @code
342 @item --drill-copper
343 Draw drill holes in pins / vias, instead of leaving solid copper.
344 @end ftable
345 %end-doc
347 {N_("drill-copper"),
348 N_("Draw drill holes in pins / vias, instead of leaving solid copper"),
349 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
350 #define HA_drillcopper 16
352 /* %start-doc options "91 Postscript Export"
353 @ftable @code
354 @cindex show-legend
355 @item --show-legend
356 Print file name and scale on printout.
357 @end ftable
358 %end-doc
360 {N_("show-legend"), N_("Print file name and scale on printout"),
361 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
362 #define HA_legend 17
365 #define NUM_OPTIONS (sizeof(ps_attribute_list)/sizeof(ps_attribute_list[0]))
367 REGISTER_ATTRIBUTES (ps_attribute_list)
369 /* All file-scope data is in global struct */
370 static struct {
371 double calibration_x, calibration_y;
373 FILE *f;
374 int pagecount;
375 Coord linewidth;
376 bool print_group[MAX_GROUP];
377 bool print_layer[MAX_LAYER];
378 double fade_ratio;
379 bool multi_file;
380 Coord media_width, media_height, ps_width, ps_height;
382 const char *filename;
383 bool drill_helper;
384 bool align_marks;
385 bool outline;
386 bool mirror;
387 bool fillpage;
388 bool automirror;
389 bool incolor;
390 bool doing_toc;
391 Coord bloat;
392 bool invert;
393 int media_idx;
394 bool drillcopper;
395 bool legend;
397 LayerType *outline_layer;
399 double scale_factor;
401 BoxType region;
403 HID_Attr_Val ps_values[NUM_OPTIONS];
405 bool is_mask;
406 bool is_drill;
407 bool is_assy;
408 bool is_copper;
409 bool is_paste;
410 } global;
412 static HID_Attribute *
413 ps_get_export_options (int *n)
415 static char *last_made_filename = 0;
416 if (PCB)
417 derive_default_filename(PCB->Filename, &ps_attribute_list[HA_psfile], ".ps", &last_made_filename);
419 if (n)
420 *n = NUM_OPTIONS;
421 return ps_attribute_list;
424 static int
425 layer_stack_sort (const void *va, const void *vb)
427 int a_layer = *(int *) va;
428 int b_layer = *(int *) vb;
429 int a_group = GetLayerGroupNumberByNumber (a_layer);
430 int b_group = GetLayerGroupNumberByNumber (b_layer);
432 if (b_group != a_group)
433 return b_group - a_group;
435 return b_layer - a_layer;
438 void
439 ps_start_file (FILE *f)
441 time_t currenttime = time( NULL );
443 fprintf (f, "%%!PS-Adobe-3.0\n");
445 /* Document Structuring Conventions (DCS): */
447 /* Start General Header Comments: */
450 * %%Title DCS provides text title for the document that is useful
451 * for printing banner pages.
453 fprintf (f, "%%%%Title: %s\n", PCB->Filename);
456 * %%CreationDate DCS indicates the date and time the document was
457 * created. Neither the date nor time need be in any standard
458 * format. This comment is meant to be used purely for informational
459 * purposes, such as printing on banner pages.
461 fprintf (f, "%%%%CreationDate: %s", asctime (localtime (&currenttime)));
464 * %%Creator DCS indicates the document creator, usually the name of
465 * the document composition software.
467 fprintf (f, "%%%%Creator: PCB release: %s " VERSION "\n", Progname);
470 * %%Version DCS comment can be used to note the version and
471 * revision number of a document or resource. A document manager may
472 * wish to provide version control services, or allow substitution
473 * of compatible versions/revisions of a resource or document.
475 * The format should be in the form of 'procname':
476 * <procname>::= < name> < version> < revision>
477 * < name> ::= < text>
478 * < version> ::= < real>
479 * < revision> ::= < uint>
481 * If a version numbering scheme is not used, these fields should
482 * still be filled with a dummy value of 0.
484 * There is currently no code in PCB to manage this revision number.
487 fprintf (f, "%%%%Version: (PCB %s " VERSION ") 0.0 0\n", Progname );
491 * %%PageOrder DCS is intended to help document managers determine
492 * the order of pages in the document file, which in turn enables a
493 * document manager optionally to reorder the pages. 'Ascend'-The
494 * pages are in ascending order for example, 1-2-3-4-5-6.
496 fprintf (f, "%%%%PageOrder: Ascend\n" );
499 * %%Pages: < numpages> | (atend) < numpages> ::= < uint> (Total
500 * %%number of pages)
502 * %%Pages DCS defines the number of virtual pages that a document
503 * will image. (atend) defers the count until the end of the file,
504 * which is useful for dynamically generated contents.
506 fprintf (f, "%%%%Pages: (atend)\n" );
509 * %%DocumentMedia: <name> <width> <height> <weight> <color> <type>
511 * Substitute 0 or "" for N/A. Width and height are in points
512 * (1/72").
514 * Media sizes are in PCB units
516 pcb_fprintf (f, "%%%%DocumentMedia: %s %mi %mi 0 \"\" \"\"\n",
517 media_data[global.media_idx].name,
518 72 * media_data[global.media_idx].Width,
519 72 * media_data[global.media_idx].Height);
520 pcb_fprintf (f, "%%%%DocumentPaperSizes: %s\n", media_data[global.media_idx].name);
522 /* End General Header Comments. */
524 /* General Body Comments go here. Currently there are none. */
527 * %%EndComments DCS indicates an explicit end to the header
528 * comments of the document. All global DCS's must preceded
529 * this. A blank line gives an implicit end to the comments.
531 fprintf (f, "%%%%EndComments\n\n" );
534 static void
535 ps_end_file (FILE *f)
538 * %%Trailer DCS must only occur once at the end of the document
539 * script. Any post-processing or cleanup should be contained in
540 * the trailer of the document, which is anything that follows the
541 * %%Trailer comment. Any of the document level structure comments
542 * that were deferred by using the (atend) convention must be
543 * mentioned in the trailer of the document after the %%Trailer
544 * comment.
546 fprintf (f, "%%%%Trailer\n" );
549 * %%Pages was deferred until the end of the document via the
550 * (atend) mentioned, in the General Header section.
552 fprintf (f, "%%%%Pages: %d\n", global.pagecount);
555 * %%EOF DCS signifies the end of the document. When the document
556 * manager sees this comment, it issues an end-of-file signal to the
557 * PostScript interpreter. This is done so system-dependent file
558 * endings, such as Control-D and end-of-file packets, do not
559 * confuse the PostScript interpreter.
561 fprintf (f, "%%%%EOF\n" );
564 static FILE *
565 psopen (const char *base, const char *which)
567 FILE *ps_open_file;
568 char *buf, *suff, *buf2;
570 if (!global.multi_file)
571 return fopen (base, "w");
573 buf = (char *)malloc (strlen (base) + strlen (which) + 5);
575 suff = (char *)strrchr (base, '.');
576 if (suff)
578 strcpy (buf, base);
579 buf2 = strrchr (buf, '.');
580 sprintf(buf2, ".%s.%s", which, suff+1);
582 else
584 sprintf(buf, "%s.%s.ps", base, which);
586 printf("PS: open %s\n", buf);
587 ps_open_file = fopen(buf, "w");
588 free (buf);
589 return ps_open_file;
592 /* This is used by other HIDs that use a postscript format, like lpr
593 or eps. */
594 void
595 ps_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
597 int i;
598 static int saved_layer_stack[MAX_LAYER];
599 FlagType save_thindraw;
601 save_thindraw = PCB->Flags;
602 CLEAR_FLAG(THINDRAWFLAG, PCB);
603 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
604 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
606 global.f = the_file;
607 global.drill_helper = options[HA_drillhelper].int_value;
608 global.align_marks = options[HA_alignmarks].int_value;
609 global.outline = options[HA_outline].int_value;
610 global.mirror = options[HA_mirror].int_value;
611 global.fillpage = options[HA_fillpage].int_value;
612 global.automirror = options[HA_automirror].int_value;
613 global.incolor = options[HA_color].int_value;
614 global.bloat = options[HA_psbloat].coord_value;
615 global.invert = options[HA_psinvert].int_value;
616 global.fade_ratio = CLAMP (options[HA_psfade].real_value, 0, 1);
617 global.media_idx = options[HA_media].int_value;
618 global.media_width = media_data[global.media_idx].Width;
619 global.media_height = media_data[global.media_idx].Height;
620 global.ps_width = global.media_width
621 - 2.0 * media_data[global.media_idx].MarginX;
622 global.ps_height = global.media_height
623 - 2.0 * media_data[global.media_idx].MarginY;
624 global.scale_factor = options[HA_scale].real_value;
625 global.calibration_x = options[HA_xcalib].real_value;
626 global.calibration_y = options[HA_ycalib].real_value;
627 global.drillcopper = options[HA_drillcopper].int_value;
628 global.legend = options[HA_legend].int_value;
630 if (the_file)
631 ps_start_file (the_file);
633 if (global.fillpage)
635 double zx, zy;
636 if (PCB->MaxWidth > PCB->MaxHeight)
638 zx = global.ps_height / PCB->MaxWidth;
639 zy = global.ps_width / PCB->MaxHeight;
641 else
643 zx = global.ps_height / PCB->MaxHeight;
644 zy = global.ps_width / PCB->MaxWidth;
646 global.scale_factor *= MIN (zx, zy);
649 memset (global.print_group, 0, sizeof (global.print_group));
650 memset (global.print_layer, 0, sizeof (global.print_layer));
652 global.outline_layer = NULL;
654 for (i = 0; i < max_copper_layer; i++)
656 LayerType *layer = PCB->Data->Layer + i;
657 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
658 global.print_group[GetLayerGroupNumberByNumber (i)] = 1;
660 if (strcmp (layer->Name, "outline") == 0 ||
661 strcmp (layer->Name, "route") == 0)
663 global.outline_layer = layer;
666 global.print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
667 global.print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
668 for (i = 0; i < max_copper_layer; i++)
669 if (global.print_group[GetLayerGroupNumberByNumber (i)])
670 global.print_layer[i] = 1;
672 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
673 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
675 global.linewidth = -1;
676 /* reset static vars */
677 ps_set_layer (NULL, 0, -1);
678 use_gc (NULL);
680 global.region.X1 = 0;
681 global.region.Y1 = 0;
682 global.region.X2 = PCB->MaxWidth;
683 global.region.Y2 = PCB->MaxHeight;
685 if (!global.multi_file)
687 /* %%Page DSC requires both a label and an ordinal */
688 fprintf (the_file, "%%%%Page: TableOfContents 1\n");
689 fprintf (the_file, "/Times-Roman findfont 24 scalefont setfont\n");
690 fprintf (the_file, "/rightshow { /s exch def s stringwidth pop -1 mul 0 rmoveto s show } def\n");
691 fprintf (the_file, "/y 72 9 mul def /toc { 100 y moveto show /y y 24 sub def } bind def\n");
692 fprintf (the_file, "/tocp { /y y 12 sub def 90 y moveto rightshow } bind def\n");
694 global.doing_toc = 1;
695 global.pagecount = 1; /* 'pagecount' is modified by hid_expose_callback() call */
696 hid_expose_callback (&ps_hid, &global.region, 0);
699 global.pagecount = 1; /* Reset 'pagecount' if single file */
700 global.doing_toc = 0;
701 ps_set_layer (NULL, 0, -1); /* reset static vars */
702 hid_expose_callback (&ps_hid, &global.region, 0);
704 if (the_file)
705 fprintf (the_file, "showpage\n");
707 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
708 PCB->Flags = save_thindraw;
711 static void
712 ps_do_export (HID_Attr_Val * options)
714 FILE *fh;
715 int save_ons[MAX_LAYER + 2];
716 int i;
718 if (!options)
720 ps_get_export_options (0);
721 for (i = 0; i < NUM_OPTIONS; i++)
722 global.ps_values[i] = ps_attribute_list[i].default_val;
723 options = global.ps_values;
726 global.filename = options[HA_psfile].str_value;
727 if (!global.filename)
728 global.filename = "pcb-out.ps";
730 global.multi_file = options[HA_multifile].int_value;
732 if (global.multi_file)
733 fh = 0;
734 else
736 fh = psopen (global.filename, "toc");
737 if (!fh)
739 perror (global.filename);
740 return;
744 hid_save_and_show_layer_ons (save_ons);
745 ps_hid_export_to_file (fh, options);
746 hid_restore_layer_ons (save_ons);
748 global.multi_file = 0;
749 if (fh)
751 ps_end_file (fh);
752 fclose (fh);
756 static void
757 ps_parse_arguments (int *argc, char ***argv)
759 hid_register_attributes (ps_attribute_list, NUM_OPTIONS);
760 hid_parse_command_line (argc, argv);
763 static void
764 corner (FILE *fh, Coord x, Coord y, int dx, int dy)
766 Coord len = MIL_TO_COORD (2000);
767 Coord len2 = MIL_TO_COORD (200);
768 Coord thick = 0;
770 * Originally 'thick' used thicker lines. Currently is uses
771 * Postscript's "device thin" line - i.e. zero width means one
772 * device pixel. The code remains in case you want to make them
773 * thicker - it needs to offset everything so that the *edge* of the
774 * thick line lines up with the edge of the board, not the *center*
775 * of the thick line.
778 pcb_fprintf (fh, "gsave %mi setlinewidth %mi %mi translate %d %d scale\n",
779 thick * 2, x, y, dx, dy);
780 pcb_fprintf (fh, "%mi %mi moveto %mi %mi %mi 0 90 arc %mi %mi lineto\n",
781 len, thick, thick, thick, len2 + thick, thick, len);
782 if (dx < 0 && dy < 0)
783 pcb_fprintf (fh, "%mi %mi moveto 0 %mi rlineto\n", len2 * 2 + thick, thick, -len2);
784 fprintf (fh, "stroke grestore\n");
787 static int
788 ps_set_layer (const char *name, int group, int empty)
790 static int lastgroup = -1;
791 time_t currenttime;
792 int idx = (group >= 0 && group < max_group)
793 ? PCB->LayerGroups.Entries[group][0]
794 : group;
795 if (name == 0)
796 name = PCB->Data->Layer[idx].Name;
798 if (empty == -1)
799 lastgroup = -1;
800 if (empty)
801 return 0;
803 if (idx >= 0 && idx < max_copper_layer && !global.print_layer[idx])
804 return 0;
806 if (strcmp (name, "invisible") == 0)
807 return 0;
809 global.is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
810 global.is_mask = (SL_TYPE (idx) == SL_MASK);
811 global.is_assy = (SL_TYPE (idx) == SL_ASSY);
812 global.is_copper = (SL_TYPE (idx) == 0);
813 global.is_paste = (SL_TYPE (idx) == SL_PASTE);
814 #if 0
815 printf ("Layer %s group %d drill %d mask %d\n", name, group, global.is_drill,
816 global.is_mask);
817 #endif
819 if (global.doing_toc)
821 if (group < 0 || group != lastgroup)
823 if (global.pagecount == 1)
825 currenttime = time (NULL);
826 fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
828 fprintf (global.f, "(%d.) tocp\n", global.pagecount);
829 fprintf (global.f, "(Table of Contents \\(This Page\\)) toc\n" );
831 fprintf (global.f, "(Created on %s) toc\n", asctime (localtime (&currenttime)));
832 fprintf (global.f, "( ) tocp\n" );
835 global.pagecount++;
836 lastgroup = group;
837 fprintf (global.f, "(%d.) tocp\n", global.pagecount);
839 fprintf (global.f, "(%s) toc\n", name);
840 return 0;
843 if (group < 0 || group != lastgroup)
845 double boffset;
846 int mirror_this = 0;
847 lastgroup = group;
849 if (global.pagecount != 0)
851 pcb_fprintf (global.f, "showpage\n");
853 global.pagecount++;
854 if (global.multi_file)
856 if (global.f)
858 ps_end_file (global.f);
859 fclose (global.f);
861 global.f = psopen (global.filename, layer_type_to_file_name (idx, FNS_fixed));
862 if (!global.f)
864 perror (global.filename);
865 return 0;
868 ps_start_file (global.f);
872 * %%Page DSC comment marks the beginning of the PostScript
873 * language instructions that describe a particular
874 * page. %%Page: requires two arguments: a page label and a
875 * sequential page number. The label may be anything, but the
876 * ordinal page number must reflect the position of that page in
877 * the body of the PostScript file and must start with 1, not 0.
879 fprintf (global.f, "%%%%Page: %s %d\n", layer_type_to_file_name(idx, FNS_fixed), global.pagecount);
881 if (global.mirror)
882 mirror_this = !mirror_this;
883 if (global.automirror
885 ((idx >= 0 && group == GetLayerGroupNumberBySide (BOTTOM_SIDE))
886 || (idx < 0 && SL_SIDE (idx) == SL_BOTTOM_SIDE)))
887 mirror_this = !mirror_this;
889 fprintf (global.f, "/Helvetica findfont 10 scalefont setfont\n");
890 if (global.legend)
892 fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
893 if (PCB->Name)
894 fprintf (global.f, "30 41 moveto (%s, %s) show\n",
895 PCB->Name, layer_type_to_file_name (idx, FNS_fixed));
896 else
897 fprintf (global.f, "30 41 moveto (%s) show\n",
898 layer_type_to_file_name (idx, FNS_fixed));
899 if (mirror_this)
900 fprintf (global.f, "( \\(mirrored\\)) show\n");
902 if (global.fillpage)
903 fprintf (global.f, "(, not to scale) show\n");
904 else
905 fprintf (global.f, "(, scale = 1:%.3f) show\n", global.scale_factor);
907 fprintf (global.f, "newpath\n");
909 pcb_fprintf (global.f, "72 72 scale %mi %mi translate\n",
910 global.media_width / 2, global.media_height / 2);
912 boffset = global.media_height / 2;
913 if (PCB->MaxWidth > PCB->MaxHeight)
915 fprintf (global.f, "90 rotate\n");
916 boffset = global.media_width / 2;
917 fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_y, global.calibration_x);
919 else
920 fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_x, global.calibration_y);
922 if (mirror_this)
923 fprintf (global.f, "1 -1 scale\n");
925 fprintf (global.f, "%g dup neg scale\n",
926 (SL_TYPE (idx) == SL_FAB) ? 1.0 : global.scale_factor);
927 pcb_fprintf (global.f, "%mi %mi translate\n", -PCB->MaxWidth / 2, -PCB->MaxHeight / 2);
929 /* Keep the drill list from falling off the left edge of the paper,
930 * even if it means some of the board falls off the right edge.
931 * If users don't want to make smaller boards, or use fewer drill
932 * sizes, they can always ignore this sheet. */
933 if (SL_TYPE (idx) == SL_FAB) {
934 Coord natural = boffset - MIL_TO_COORD(500) - PCB->MaxHeight / 2;
935 Coord needed = PrintFab_overhang ();
936 pcb_fprintf (global.f, "%% PrintFab overhang natural %mi, needed %mi\n", natural, needed);
937 if (needed > natural)
938 pcb_fprintf (global.f, "0 %mi translate\n", needed - natural);
941 if (global.invert)
943 fprintf (global.f, "/gray { 1 exch sub setgray } bind def\n");
944 fprintf (global.f,
945 "/rgb { 1 1 3 { pop 1 exch sub 3 1 roll } for setrgbcolor } bind def\n");
947 else
949 fprintf (global.f, "/gray { setgray } bind def\n");
950 fprintf (global.f, "/rgb { setrgbcolor } bind def\n");
953 if ((global.outline && !global.outline_layer) || global.invert)
955 pcb_fprintf (global.f,
956 "0 setgray 0 setlinewidth 0 0 moveto 0 "
957 "%mi lineto %mi %mi lineto %mi 0 lineto closepath %s\n",
958 PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight, PCB->MaxWidth,
959 global.invert ? "fill" : "stroke");
962 if (global.align_marks)
964 corner (global.f, 0, 0, -1, -1);
965 corner (global.f, PCB->MaxWidth, 0, 1, -1);
966 corner (global.f, PCB->MaxWidth, PCB->MaxHeight, 1, 1);
967 corner (global.f, 0, PCB->MaxHeight, -1, 1);
970 global.linewidth = -1;
971 use_gc (NULL); /* reset static vars */
973 fprintf (global.f,
974 "/ts 1 def\n"
975 "/ty ts neg def /tx 0 def /Helvetica findfont ts scalefont setfont\n"
976 "/t { moveto lineto stroke } bind def\n"
977 "/dr { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
978 " x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath stroke } bind def\n"
979 "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
980 " x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n"
981 "/c { 0 360 arc fill } bind def\n"
982 "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
983 if (global.drill_helper)
984 pcb_fprintf (global.f,
985 "/dh { gsave %mi setlinewidth 0 gray %mi 0 360 arc stroke grestore} bind def\n",
986 (Coord) MIN_PINORVIAHOLE, (Coord) (MIN_PINORVIAHOLE * 3 / 2));
988 #if 0
989 /* Try to outsmart ps2pdf's heuristics for page rotation, by putting
990 * text on all pages -- even if that text is blank */
991 if (SL_TYPE (idx) != SL_FAB)
992 fprintf (global.f,
993 "gsave tx ty translate 1 -1 scale 0 0 moveto (Layer %s) show grestore newpath /ty ty ts sub def\n",
994 name);
995 else
996 fprintf (global.f, "gsave tx ty translate 1 -1 scale 0 0 moveto ( ) show grestore newpath /ty ty ts sub def\n");
997 #endif
999 /* If we're printing a copper layer other than the outline layer,
1000 and we want to "print outlines", and we have an outline layer,
1001 print the outline layer on this layer also. */
1002 if (global.outline &&
1003 global.is_copper &&
1004 global.outline_layer != NULL &&
1005 global.outline_layer != PCB->Data->Layer+idx &&
1006 strcmp (name, "outline") != 0 &&
1007 strcmp (name, "route") != 0
1010 DrawLayer (global.outline_layer, &global.region);
1013 return 1;
1016 static hidGC
1017 ps_make_gc (void)
1019 hidGC gc = (hidGC) calloc (1, sizeof (struct ps_gc_struct));
1020 psGC ps_gc = (psGC)gc;
1022 gc->hid = &ps_hid;
1023 gc->hid_draw = &ps_graphics;
1025 ps_gc->cap = Trace_Cap;
1027 return gc;
1030 static void
1031 ps_destroy_gc (hidGC gc)
1033 free (gc);
1036 static void
1037 ps_use_mask (enum mask_mode mode)
1039 /* does nothing */
1042 static void
1043 ps_set_color (hidGC gc, const char *name)
1045 psGC ps_gc = (psGC)gc;
1047 if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
1049 ps_gc->r = ps_gc->g = ps_gc->b = 255;
1050 ps_gc->erase = 1;
1052 else if (global.incolor)
1054 int r, g, b;
1055 sscanf (name + 1, "%02x%02x%02x", &r, &g, &b);
1056 ps_gc->r = r;
1057 ps_gc->g = g;
1058 ps_gc->b = b;
1059 ps_gc->erase = 0;
1061 else
1063 ps_gc->r = ps_gc->g = ps_gc->b = 0;
1064 ps_gc->erase = 0;
1068 static void
1069 ps_set_line_cap (hidGC gc, EndCapStyle style)
1071 psGC ps_gc = (psGC)gc;
1073 ps_gc->cap = style;
1076 static void
1077 ps_set_line_width (hidGC gc, Coord width)
1079 psGC ps_gc = (psGC)gc;
1081 ps_gc->width = width;
1084 static void
1085 ps_set_draw_xor (hidGC gc, int xor_)
1090 static void
1091 ps_set_draw_faded (hidGC gc, int faded)
1093 psGC ps_gc = (psGC)gc;
1095 ps_gc->faded = faded;
1098 static void
1099 use_gc (hidGC gc)
1101 psGC ps_gc = (psGC)gc;
1102 static int lastcap = -1;
1103 static int lastcolor = -1;
1105 if (gc == NULL)
1107 lastcap = lastcolor = -1;
1108 return;
1110 if (gc->hid != &ps_hid)
1112 fprintf (stderr, "Fatal: GC from another HID passed to ps HID\n");
1113 abort ();
1115 if (global.linewidth != ps_gc->width)
1117 pcb_fprintf (global.f, "%mi setlinewidth\n",
1118 ps_gc->width + (ps_gc->erase ? -2 : 2) * global.bloat);
1119 global.linewidth = ps_gc->width;
1121 if (lastcap != ps_gc->cap)
1123 int c;
1124 switch (ps_gc->cap)
1126 case Round_Cap:
1127 case Trace_Cap:
1128 c = 1;
1129 break;
1130 default:
1131 case Square_Cap:
1132 c = 2;
1133 break;
1135 fprintf (global.f, "%d setlinecap %d setlinejoin\n", c, c);
1136 lastcap = ps_gc->cap;
1138 #define CBLEND(gc) (((ps_gc->r)<<24)|((ps_gc->g)<<16)|((ps_gc->b)<<8)|(ps_gc->faded))
1139 if (lastcolor != CBLEND (gc))
1141 if (global.is_drill || global.is_mask)
1143 fprintf (global.f, "%d gray\n", ps_gc->erase ? 0 : 1);
1144 lastcolor = 0;
1146 else
1148 double r, g, b;
1149 r = ps_gc->r;
1150 g = ps_gc->g;
1151 b = ps_gc->b;
1152 if (ps_gc->faded)
1154 r = (1 - global.fade_ratio) * 255 + global.fade_ratio * r;
1155 g = (1 - global.fade_ratio) * 255 + global.fade_ratio * g;
1156 b = (1 - global.fade_ratio) * 255 + global.fade_ratio * b;
1158 if (ps_gc->r == ps_gc->g && ps_gc->g == ps_gc->b)
1159 fprintf (global.f, "%g gray\n", r / 255.0);
1160 else
1161 fprintf (global.f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
1162 lastcolor = CBLEND (gc);
1167 static void
1168 ps_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1170 use_gc (gc);
1171 pcb_fprintf (global.f, "%mi %mi %mi %mi dr\n", x1, y1, x2, y2);
1174 static void ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
1175 static void ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
1177 static void
1178 ps_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1180 psGC ps_gc = (psGC)gc;
1182 #if 0
1183 /* If you're etching your own paste mask, this will reduce the
1184 amount of brass you need to etch by drawing outlines for large
1185 pads. See also ps_fill_rect. */
1186 if (is_paste && ps_gc->width > 2500 && ps_gc->cap == Square_Cap
1187 && (x1 == x2 || y1 == y2))
1189 Coord t, w;
1190 if (x1 > x2)
1191 { t = x1; x1 = x2; x2 = t; }
1192 if (y1 > y2)
1193 { t = y1; y1 = y2; y2 = t; }
1194 w = ps_gc->width/2;
1195 ps_fill_rect (gc, x1-w, y1-w, x2+w, y2+w);
1196 return;
1198 #endif
1199 if (x1 == x2 && y1 == y2)
1201 Coord w = ps_gc->width / 2;
1202 if (ps_gc->cap == Square_Cap)
1203 ps_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
1204 else
1205 ps_fill_circle (gc, x1, y1, w);
1206 return;
1208 use_gc (gc);
1209 pcb_fprintf (global.f, "%mi %mi %mi %mi t\n", x1, y1, x2, y2);
1212 static void
1213 ps_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1214 Angle start_angle, Angle delta_angle)
1216 Angle sa, ea;
1217 if (delta_angle > 0)
1219 sa = start_angle;
1220 ea = start_angle + delta_angle;
1222 else
1224 sa = start_angle + delta_angle;
1225 ea = start_angle;
1227 #if 0
1228 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1229 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1230 #endif
1231 use_gc (gc);
1232 pcb_fprintf (global.f, "%ma %ma %mi %mi %mi %mi %g a\n",
1233 sa, ea, -width, height, cx, cy,
1234 (double) (global.linewidth + 2 * global.bloat) / (double) width);
1237 static void
1238 ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1240 psGC ps_gc = (psGC)gc;
1242 use_gc (gc);
1243 if (!ps_gc->erase || !global.is_copper || global.drillcopper)
1245 if (ps_gc->erase && global.is_copper && global.drill_helper
1246 && radius >= PCB->minDrill / 4)
1247 radius = PCB->minDrill / 4;
1248 pcb_fprintf (global.f, "%mi %mi %mi c\n",
1249 cx, cy, radius + (ps_gc->erase ? -1 : 1) * global.bloat);
1253 static void
1254 ps_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1256 int i;
1257 char *op = "moveto";
1258 use_gc (gc);
1259 for (i = 0; i < n_coords; i++)
1261 pcb_fprintf (global.f, "%mi %mi %s\n", x[i], y[i], op);
1262 op = "lineto";
1264 fprintf (global.f, "fill\n");
1267 static void
1268 fill_polyarea (hidGC gc, POLYAREA * pa, const BoxType * clip_box)
1270 /* Ignore clip_box, just draw everything */
1272 VNODE *v;
1273 PLINE *pl;
1274 char *op;
1276 use_gc (gc);
1278 pl = pa->contours;
1282 v = pl->head.next;
1283 op = "moveto";
1286 pcb_fprintf (global.f, "%mi %mi %s\n", v->point[0], v->point[1], op);
1287 op = "lineto";
1289 while ((v = v->next) != pl->head.next);
1291 while ((pl = pl->next) != NULL);
1293 fprintf (global.f, "fill\n");
1296 static void
1297 ps_draw_pcb_polygon (hidGC gc, PolygonType * poly, const BoxType * clip_box)
1299 fill_polyarea (gc, poly->Clipped, clip_box);
1300 if (TEST_FLAG (FULLPOLYFLAG, poly))
1302 POLYAREA *pa;
1304 for (pa = poly->Clipped->f; pa != poly->Clipped; pa = pa->f)
1305 fill_polyarea (gc, pa, clip_box);
1309 static void
1310 ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1312 use_gc (gc);
1313 if (x1 > x2)
1315 Coord t = x1;
1316 x1 = x2;
1317 x2 = t;
1319 if (y1 > y2)
1321 Coord t = y1;
1322 y1 = y2;
1323 y2 = t;
1325 #if 0
1326 /* See comment in ps_draw_line. */
1327 if (is_paste && (x2-x1)>2500 && (y2-y1)>2500)
1329 linewidth = 1000;
1330 lastcap = Round_Cap;
1331 fprintf(f, "1000 setlinewidth 1 setlinecap 1 setlinejoin\n");
1332 fprintf(f, "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
1333 x1+500-bloat, y1+500-bloat,
1334 x1+500-bloat, y2-500+bloat,
1335 x2-500+bloat, y2-500+bloat,
1336 x2-500+bloat, y1+500-bloat);
1337 return;
1339 #endif
1340 pcb_fprintf (global.f, "%mi %mi %mi %mi r\n",
1341 x1 - global.bloat, y1 - global.bloat,
1342 x2 + global.bloat, y2 + global.bloat);
1345 HID_Attribute ps_calib_attribute_list[] = {
1346 {N_("lprcommand"), N_("Command to print"),
1347 HID_String, 0, 0, {0, 0, 0}, 0, 0},
1350 static const char * const calib_lines[] = {
1351 "%!PS-Adobe-3.0\n",
1352 "%%Title: Calibration Page\n",
1353 "%%PageOrder: Ascend\n",
1354 "%%Pages: 1\n",
1355 "%%EndComments\n",
1356 "\n",
1357 "%%Page: Calibrate 1\n",
1358 "72 72 scale\n",
1359 "\n",
1360 "0 setlinewidth\n",
1361 "0.375 0.375 moveto\n",
1362 "8.125 0.375 lineto\n",
1363 "8.125 10.625 lineto\n",
1364 "0.375 10.625 lineto\n",
1365 "closepath stroke\n",
1366 "\n",
1367 "0.5 0.5 translate\n",
1368 "0.001 setlinewidth\n",
1369 "\n",
1370 "/Times-Roman findfont 0.2 scalefont setfont\n",
1371 "\n",
1372 "/sign {\n",
1373 " 0 lt { -1 } { 1 } ifelse\n",
1374 "} def\n",
1375 "\n",
1376 "/cbar {\n",
1377 " /units exch def\n",
1378 " /x exch def\n",
1379 " /y exch def \n",
1380 "\n",
1381 " /x x sign 0.5 mul def\n",
1382 "\n",
1383 " 0 setlinewidth\n",
1384 " newpath x y 0.25 0 180 arc gsave 0.85 setgray fill grestore closepath stroke\n",
1385 " newpath x 0 0.25 180 360 arc gsave 0.85 setgray fill grestore closepath stroke\n",
1386 " 0.001 setlinewidth\n",
1387 "\n",
1388 " x 0 moveto\n",
1389 " x y lineto\n",
1390 "% -0.07 -0.2 rlineto 0.14 0 rmoveto -0.07 0.2 rlineto\n",
1391 " x y lineto\n",
1392 " -0.1 0 rlineto 0.2 0 rlineto\n",
1393 " stroke\n",
1394 " x 0 moveto\n",
1395 "% -0.07 0.2 rlineto 0.14 0 rmoveto -0.07 -0.2 rlineto\n",
1396 " x 0 moveto\n",
1397 " -0.1 0 rlineto 0.2 0 rlineto\n",
1398 " stroke\n",
1399 "\n",
1400 " x 0.1 add\n",
1401 " y 0.2 sub moveto\n",
1402 " units show\n",
1403 "} bind def\n",
1404 "\n",
1405 "/y 9 def\n",
1406 "/t {\n",
1407 " /str exch def\n",
1408 " 1.5 y moveto str show\n",
1409 " /y y 0.25 sub def\n",
1410 "} bind def\n",
1411 "\n",
1412 "(Please measure between the flat faces of ONE pair of semi-circles on)t\n",
1413 "(both X and Y in the indicated units. Enter these values as X and Y)t\n",
1414 "(respectively. One member of each pair must be one of the semicircles)t\n",
1415 "(in the lower left corner. Nominal lengths on X are 4 in, 15 cm and 7.5 in.)t\n",
1416 "(Nominal lengths on Y are 4 in, 20 cm and 10 in.)t\n",
1417 "()t\n",
1418 "(The large box is 10.25 by 7.75 inches and is not used for calibration.)t\n", "\n",
1419 "/in { } bind def\n",
1420 "/cm { 2.54 div } bind def\n",
1421 "/mm { 25.4 div } bind def\n",
1422 "\n",
1426 static int
1427 guess(double val, double close_to, double *calib)
1429 if (val >= close_to * 0.9
1430 && val <= close_to * 1.1)
1432 *calib = close_to / val;
1433 return 0;
1435 return 1;
1438 void
1439 ps_calibrate_1 (double xval, double yval, int use_command)
1441 HID_Attr_Val vals[3];
1442 FILE *ps_cal_file;
1443 int used_popen = 0, c;
1445 if (xval > 0 && yval > 0)
1447 if (guess (xval, 4, &global.calibration_x))
1448 if (guess (xval, 15, &global.calibration_x))
1449 if (guess (xval, 7.5, &global.calibration_x))
1451 if (xval < 2)
1452 ps_attribute_list[HA_xcalib].default_val.real_value =
1453 global.calibration_x = xval;
1454 else
1455 Message(_("X value of %g is too far off.\n"
1456 "Expecting it near: %.1f, %.1f, %.1f, %.1f\n"),
1457 xval, 1.0, 4.0, 15.0, 7.5);
1459 if (guess (yval, 4, &global.calibration_y))
1460 if (guess (yval, 20, &global.calibration_y))
1461 if (guess (yval, 10, &global.calibration_y))
1463 if (yval < 2)
1464 ps_attribute_list[HA_ycalib].default_val.real_value =
1465 global.calibration_y = yval;
1466 else
1467 Message(_("Y value of %g is too far off.\n"
1468 "Expecting it near: %.1f, %.1f, %.1f, %.1f\n"),
1469 yval, 1.0, 4.0, 20.0, 10.0);
1471 return;
1474 if (ps_calib_attribute_list[0].default_val.str_value == NULL)
1476 ps_calib_attribute_list[0].default_val.str_value = strdup ("lpr");
1479 if (gui->attribute_dialog (ps_calib_attribute_list, 1, vals, _("Print Calibration Page"), _("Generates a printer calibration page")))
1480 return;
1482 if (use_command || strchr (vals[0].str_value, '|'))
1484 const char *cmd = vals[0].str_value;
1485 while (*cmd == ' ' || *cmd == '|')
1486 cmd ++;
1487 ps_cal_file = popen (cmd, "w");
1488 used_popen = 1;
1490 else
1491 ps_cal_file = fopen (vals[0].str_value, "w");
1493 for (c=0; calib_lines[c]; c++)
1494 fputs(calib_lines[c], ps_cal_file);
1496 fprintf (ps_cal_file, "4 in 0.5 (Y in nom=4) cbar\n");
1497 fprintf (ps_cal_file, "20 cm 1.5 (Y cm nom=20) cbar\n");
1498 fprintf (ps_cal_file, "10 in 2.5 (Y in nom=10) cbar\n");
1499 fprintf (ps_cal_file, "-90 rotate\n");
1500 fprintf (ps_cal_file, "4 in -0.5 (X in nom=4) cbar\n");
1501 fprintf (ps_cal_file, "15 cm -1.5 (X cm nom=15) cbar\n");
1502 fprintf (ps_cal_file, "7.5 in -2.5 (X in nom=7.5) cbar\n");
1503 fprintf (ps_cal_file, "showpage\n");
1505 fprintf (ps_cal_file, "%%%%EOF\n");
1507 if (used_popen)
1508 pclose (ps_cal_file);
1509 else
1510 fclose (ps_cal_file);
1513 static void
1514 ps_calibrate (double xval, double yval)
1516 ps_calibrate_1 (xval, yval, 0);
1519 static void
1520 ps_set_crosshair (int x, int y, int action)
1524 #include "dolists.h"
1526 void ps_ps_init (HID *hid)
1528 hid->get_export_options = ps_get_export_options;
1529 hid->do_export = ps_do_export;
1530 hid->parse_arguments = ps_parse_arguments;
1531 hid->calibrate = ps_calibrate;
1532 hid->set_crosshair = ps_set_crosshair;
1535 void ps_ps_graphics_class_init (HID_DRAW_CLASS *klass)
1537 klass->set_layer = ps_set_layer;
1538 klass->make_gc = ps_make_gc;
1539 klass->destroy_gc = ps_destroy_gc;
1540 klass->use_mask = ps_use_mask;
1541 klass->set_color = ps_set_color;
1542 klass->set_line_cap = ps_set_line_cap;
1543 klass->set_line_width = ps_set_line_width;
1544 klass->set_draw_xor = ps_set_draw_xor;
1545 klass->set_draw_faded = ps_set_draw_faded;
1546 klass->draw_line = ps_draw_line;
1547 klass->draw_arc = ps_draw_arc;
1548 klass->draw_rect = ps_draw_rect;
1549 klass->fill_circle = ps_fill_circle;
1550 klass->fill_polygon = ps_fill_polygon;
1551 klass->fill_rect = ps_fill_rect;
1553 klass->draw_pcb_polygon = ps_draw_pcb_polygon;
1556 void ps_ps_graphics_init (HID_DRAW *graphics)
1560 void
1561 hid_ps_init ()
1563 memset (&ps_hid, 0, sizeof (HID));
1564 memset (&ps_graphics, 0, sizeof (HID_DRAW));
1566 common_nogui_init (&ps_hid);
1567 ps_ps_init (&ps_hid);
1569 ps_hid.struct_size = sizeof (HID);
1570 ps_hid.name = "ps";
1571 ps_hid.description = N_("Postscript export");
1572 ps_hid.exporter = 1;
1574 ps_hid.graphics = &ps_graphics;
1576 common_draw_helpers_class_init (&ps_graphics_class);
1577 ps_ps_graphics_class_init (&ps_graphics_class);
1579 ps_graphics.klass = &ps_graphics_class;
1580 ps_graphics.poly_before = true;
1581 common_draw_helpers_init (&ps_graphics);
1582 ps_ps_graphics_init (&ps_graphics);
1584 hid_register_hid (&ps_hid);
1586 hid_eps_init ();
1587 #include "ps_lists.h"