6 #include <stdarg.h> /* not used */
9 #include <assert.h> /* not used */
17 #include "pcb-printf.h"
20 #include "../hidint.h"
21 #include "hid/common/hidnogui.h"
22 #include "hid/common/draw_helpers.h"
24 #include "../../print.h"
25 #include "hid/common/hidinit.h"
27 #ifdef HAVE_LIBDMALLOC
31 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PS function %s.\n", __FUNCTION__); abort()
33 static int ps_set_layer (const char *name
, int group
, int empty
);
34 static void use_gc (hidGC gc
);
36 typedef struct hid_gc_struct
41 unsigned char r
, g
, b
;
46 static const char *medias
[] = {
47 "A0", "A1", "A2", "A3", "A4", "A5",
48 "A6", "A7", "A8", "A9", "A10",
49 "B0", "B1", "B2", "B3", "B4", "B5",
50 "B6", "B7", "B8", "B9", "B10",
51 "Letter", "11x17", "Ledger",
54 "C-Size", "D-size", "E-size",
55 "US-Business_Card", "Intl-Business_Card",
63 Coord MarginX
, MarginY
;
67 * Metric ISO sizes in mm. See http://en.wikipedia.org/wiki/ISO_paper_sizes
93 * awk '{printf(" {\"%s\", %d, %d, MARGINX, MARGINY},\n", $2, $3*100000/25.4, $5*100000/25.4)}'
95 * See http://en.wikipedia.org/wiki/Paper_size#Loose_sizes for some of the other sizes. The
96 * {A,B,C,D,E}-Size here are the ANSI sizes and not the architectural sizes.
99 #define MARGINX MIL_TO_COORD(500)
100 #define MARGINY MIL_TO_COORD(500)
102 static MediaType media_data
[] = {
103 {"A0", MM_TO_COORD(841), MM_TO_COORD(1189), MARGINX
, MARGINY
},
104 {"A1", MM_TO_COORD(594), MM_TO_COORD(841), MARGINX
, MARGINY
},
105 {"A2", MM_TO_COORD(420), MM_TO_COORD(594), MARGINX
, MARGINY
},
106 {"A3", MM_TO_COORD(297), MM_TO_COORD(420), MARGINX
, MARGINY
},
107 {"A4", MM_TO_COORD(210), MM_TO_COORD(297), MARGINX
, MARGINY
},
108 {"A5", MM_TO_COORD(148), MM_TO_COORD(210), MARGINX
, MARGINY
},
109 {"A6", MM_TO_COORD(105), MM_TO_COORD(148), MARGINX
, MARGINY
},
110 {"A7", MM_TO_COORD(74), MM_TO_COORD(105), MARGINX
, MARGINY
},
111 {"A8", MM_TO_COORD(52), MM_TO_COORD(74), MARGINX
, MARGINY
},
112 {"A9", MM_TO_COORD(37), MM_TO_COORD(52), MARGINX
, MARGINY
},
113 {"A10", MM_TO_COORD(26), MM_TO_COORD(37), MARGINX
, MARGINY
},
114 {"B0", MM_TO_COORD(1000), MM_TO_COORD(1414), MARGINX
, MARGINY
},
115 {"B1", MM_TO_COORD(707), MM_TO_COORD(1000), MARGINX
, MARGINY
},
116 {"B2", MM_TO_COORD(500), MM_TO_COORD(707), MARGINX
, MARGINY
},
117 {"B3", MM_TO_COORD(353), MM_TO_COORD(500), MARGINX
, MARGINY
},
118 {"B4", MM_TO_COORD(250), MM_TO_COORD(353), MARGINX
, MARGINY
},
119 {"B5", MM_TO_COORD(176), MM_TO_COORD(250), MARGINX
, MARGINY
},
120 {"B6", MM_TO_COORD(125), MM_TO_COORD(176), MARGINX
, MARGINY
},
121 {"B7", MM_TO_COORD(88), MM_TO_COORD(125), MARGINX
, MARGINY
},
122 {"B8", MM_TO_COORD(62), MM_TO_COORD(88), MARGINX
, MARGINY
},
123 {"B9", MM_TO_COORD(44), MM_TO_COORD(62), MARGINX
, MARGINY
},
124 {"B10", MM_TO_COORD(31), MM_TO_COORD(44), MARGINX
, MARGINY
},
125 {"Letter", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX
, MARGINY
},
126 {"11x17", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX
, MARGINY
},
127 {"Ledger", INCH_TO_COORD(17), INCH_TO_COORD(11), MARGINX
, MARGINY
},
128 {"Legal", INCH_TO_COORD(8.5), INCH_TO_COORD(14), MARGINX
, MARGINY
},
129 {"Executive", INCH_TO_COORD(7.5), INCH_TO_COORD(10), MARGINX
, MARGINY
},
130 {"A-size", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX
, MARGINY
},
131 {"B-size", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX
, MARGINY
},
132 {"C-size", INCH_TO_COORD(17), INCH_TO_COORD(22), MARGINX
, MARGINY
},
133 {"D-size", INCH_TO_COORD(22), INCH_TO_COORD(34), MARGINX
, MARGINY
},
134 {"E-size", INCH_TO_COORD(34), INCH_TO_COORD(44), MARGINX
, MARGINY
},
135 {"US-Business_Card", INCH_TO_COORD(3.5), INCH_TO_COORD(2.0), 0, 0},
136 {"Intl-Business_Card", INCH_TO_COORD(3.375), INCH_TO_COORD(2.125), 0, 0}
142 HID_Attribute ps_attribute_list
[] = {
143 /* other HIDs expect this to be first. */
145 /* %start-doc options "91 Postscript Export"
147 @item --psfile <string>
148 Name of the postscript output file. Can contain a path.
152 {"psfile", "Postscript output file",
153 HID_String
, 0, 0, {0, 0, 0}, 0, 0},
156 /* %start-doc options "91 Postscript Export"
160 Print a centering target in large drill holes.
164 {"drill-helper", "Print a centering target in large drill holes",
165 HID_Boolean
, 0, 0, {0, 0, 0}, 0, 0},
166 #define HA_drillhelper 1
168 /* %start-doc options "91 Postscript Export"
172 Print alignment marks on each sheet. This is meant to ease alignment during exposure.
176 {"align-marks", "Print alignment marks on each sheet",
177 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
178 #define HA_alignmarks 2
180 /* %start-doc options "91 Postscript Export"
183 Print the contents of the outline layer on each sheet.
187 {"outline", "Print outline on each sheet",
188 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
190 /* %start-doc options "91 Postscript Export"
197 {"mirror", "Print mirror image of every page",
198 HID_Boolean
, 0, 0, {0, 0, 0}, 0, 0},
201 /* %start-doc options "91 Postscript Export"
204 Scale output to make the board fit the page.
208 {"fill-page", "Scale board to fill page",
209 HID_Boolean
, 0, 0, {0, 0, 0}, 0, 0},
210 #define HA_fillpage 5
212 /* %start-doc options "91 Postscript Export"
215 Print mirror image of appropriate layers.
219 {"auto-mirror", "Print mirror image of appropriate layers",
220 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
221 #define HA_automirror 6
223 /* %start-doc options "91 Postscript Export"
226 Postscript output in color.
230 {"ps-color", "Prints in color",
231 HID_Boolean
, 0, 0, {0, 0, 0}, 0, 0},
234 /* %start-doc options "91 Postscript Export"
237 @item --ps-bloat <num>
238 Amount to add to trace/pad/pin edges.
242 {"ps-bloat", "Amount to add to trace/pad/pin edges",
243 HID_Coord
, -MIL_TO_COORD (100), MIL_TO_COORD (100), {0, 0, 0}, 0, 0},
246 /* %start-doc options "91 Postscript Export"
250 Draw objects as white-on-black.
254 {"ps-invert", "Draw objects as white-on-black",
255 HID_Boolean
, 0, 0, {0, 0, 0}, 0, 0},
256 #define HA_psinvert 9
258 /* %start-doc options "91 Postscript Export"
260 @item --media <media-name>
261 Size of the media, the postscript is fitted to. The parameter
262 @code{<media-name>} can be any of the standard names for paper size: @samp{A0}
263 to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
264 @samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
265 @samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
266 @samp{Intl-Business_Card}.
270 {"media", "media type",
271 HID_Enum
, 0, 0, {22, 0, 0}, medias
, 0},
274 /* %start-doc options "91 Postscript Export"
278 Fade amount for assembly drawings (0.0=missing, 1.0=solid).
282 {"psfade", "Fade amount for assembly drawings (0.0=missing, 1.0=solid)",
283 HID_Real
, 0, 1, {0, 0, 0.40}, 0, 0},
286 /* %start-doc options "91 Postscript Export"
289 Scale value to compensate for printer sizing errors (1.0 = full scale).
293 {"scale", "Scale value to compensate for printer sizing errors (1.0 = full scale)",
294 HID_Real
, 0.01, 4, {0, 0, 1.00}, 0, 0},
297 /* %start-doc options "91 Postscript Export"
301 Produce multiple files, one per page, instead of a single multi page file.
305 {"multi-file", "Produce multiple files, one per page, instead of a single file",
306 HID_Boolean
, 0, 0, {0, 0, 0.40}, 0, 0},
307 #define HA_multifile 13
309 /* %start-doc options "91 Postscript Export"
312 Paper width. Used for x-Axis calibration.
316 {"xcalib", "Paper width. Used for x-Axis calibration",
317 HID_Real
, 0, 0, {0, 0, 1.0}, 0, 0},
320 /* %start-doc options "91 Postscript Export"
323 Paper height. Used for y-Axis calibration.
327 {"ycalib", "Paper height. Used for y-Axis calibration",
328 HID_Real
, 0, 0, {0, 0, 1.0}, 0, 0},
331 /* %start-doc options "91 Postscript Export"
334 Draw drill holes in pins / vias, instead of leaving solid copper.
338 {"drill-copper", "Draw drill holes in pins / vias, instead of leaving solid copper",
339 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
340 #define HA_drillcopper 16
342 /* %start-doc options "91 Postscript Export"
346 Print file name and scale on printout.
350 {"show-legend", "Print file name and scale on printout",
351 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
355 #define NUM_OPTIONS (sizeof(ps_attribute_list)/sizeof(ps_attribute_list[0]))
357 REGISTER_ATTRIBUTES (ps_attribute_list
)
359 /* All file-scope data is in global struct */
361 double calibration_x
, calibration_y
;
366 bool print_group
[MAX_LAYER
];
367 bool print_layer
[MAX_LAYER
];
370 Coord media_width
, media_height
, ps_width
, ps_height
;
372 const char *filename
;
387 LayerType
*outline_layer
;
393 HID_Attr_Val ps_values
[NUM_OPTIONS
];
402 static HID_Attribute
*
403 ps_get_export_options (int *n
)
405 static char *last_made_filename
= 0;
407 derive_default_filename(PCB
->Filename
, &ps_attribute_list
[HA_psfile
], ".ps", &last_made_filename
);
411 return ps_attribute_list
;
415 group_for_layer (int l
)
417 if (l
< max_copper_layer
+ 2 && l
>= 0)
418 return GetLayerGroupNumberByNumber (l
);
419 /* else something unique */
420 return max_group
+ 3 + l
;
424 layer_sort (const void *va
, const void *vb
)
428 int d
= group_for_layer (b
) - group_for_layer (a
);
435 ps_start_file (FILE *f
)
437 time_t currenttime
= time( NULL
);
439 fprintf (f
, "%%!PS-Adobe-3.0\n");
441 /* Document Structuring Conventions (DCS): */
443 /* Start General Header Comments: */
446 * %%Title DCS provides text title for the document that is useful
447 * for printing banner pages.
449 fprintf (f
, "%%%%Title: %s\n", PCB
->Filename
);
452 * %%CreationDate DCS indicates the date and time the document was
453 * created. Neither the date nor time need be in any standard
454 * format. This comment is meant to be used purely for informational
455 * purposes, such as printing on banner pages.
457 fprintf (f
, "%%%%CreationDate: %s", asctime (localtime (¤ttime
)));
460 * %%Creator DCS indicates the document creator, usually the name of
461 * the document composition software.
463 fprintf (f
, "%%%%Creator: PCB release: %s " VERSION
"\n", Progname
);
466 * %%Version DCS comment can be used to note the version and
467 * revision number of a document or resource. A document manager may
468 * wish to provide version control services, or allow substitution
469 * of compatible versions/revisions of a resource or document.
471 * The format should be in the form of 'procname':
472 * <procname>::= < name> < version> < revision>
473 * < name> ::= < text>
474 * < version> ::= < real>
475 * < revision> ::= < uint>
477 * If a version numbering scheme is not used, these fields should
478 * still be filled with a dummy value of 0.
480 * There is currently no code in PCB to manage this revision number.
483 fprintf (f
, "%%%%Version: (PCB %s " VERSION
") 0.0 0\n", Progname
);
487 * %%PageOrder DCS is intended to help document managers determine
488 * the order of pages in the document file, which in turn enables a
489 * document manager optionally to reorder the pages. 'Ascend'-The
490 * pages are in ascending order for example, 1-2-3-4-5-6.
492 fprintf (f
, "%%%%PageOrder: Ascend\n" );
495 * %%Pages: < numpages> | (atend) < numpages> ::= < uint> (Total
498 * %%Pages DCS defines the number of virtual pages that a document
499 * will image. (atend) defers the count until the end of the file,
500 * which is useful for dynamically generated contents.
502 fprintf (f
, "%%%%Pages: (atend)\n" );
505 * %%DocumentMedia: <name> <width> <height> <weight> <color> <type>
507 * Substitute 0 or "" for N/A. Width and height are in points
510 * Media sizes are in PCB units
512 pcb_fprintf (f
, "%%%%DocumentMedia: %s %mi %mi 0 \"\" \"\"\n",
513 media_data
[global
.media_idx
].name
,
514 72 * media_data
[global
.media_idx
].Width
,
515 72 * media_data
[global
.media_idx
].Height
);
516 pcb_fprintf (f
, "%%%%DocumentPaperSizes: %s\n", media_data
[global
.media_idx
].name
);
518 /* End General Header Comments. */
520 /* General Body Comments go here. Currently there are none. */
523 * %%EndComments DCS indicates an explicit end to the header
524 * comments of the document. All global DCS's must preceded
525 * this. A blank line gives an implicit end to the comments.
527 fprintf (f
, "%%%%EndComments\n\n" );
531 ps_end_file (FILE *f
)
534 * %%Trailer DCS must only occur once at the end of the document
535 * script. Any post-processing or cleanup should be contained in
536 * the trailer of the document, which is anything that follows the
537 * %%Trailer comment. Any of the document level structure comments
538 * that were deferred by using the (atend) convention must be
539 * mentioned in the trailer of the document after the %%Trailer
542 fprintf (f
, "%%%%Trailer\n" );
545 * %%Pages was deferred until the end of the document via the
546 * (atend) mentioned, in the General Header section.
548 fprintf (f
, "%%%%Pages: %d\n", global
.pagecount
);
551 * %%EOF DCS signifies the end of the document. When the document
552 * manager sees this comment, it issues an end-of-file signal to the
553 * PostScript interpreter. This is done so system-dependent file
554 * endings, such as Control-D and end-of-file packets, do not
555 * confuse the PostScript interpreter.
557 fprintf (f
, "%%%%EOF\n" );
561 psopen (const char *base
, const char *which
)
564 char *buf
, *suff
, *buf2
;
566 if (!global
.multi_file
)
567 return fopen (base
, "w");
569 buf
= (char *)malloc (strlen (base
) + strlen (which
) + 5);
571 suff
= (char *)strrchr (base
, '.');
575 buf2
= strrchr (buf
, '.');
576 sprintf(buf2
, ".%s.%s", which
, suff
+1);
580 sprintf(buf
, "%s.%s.ps", base
, which
);
582 printf("PS: open %s\n", buf
);
583 ps_open_file
= fopen(buf
, "w");
588 /* This is used by other HIDs that use a postscript format, like lpr
591 ps_hid_export_to_file (FILE * the_file
, HID_Attr_Val
* options
)
594 static int saved_layer_stack
[MAX_LAYER
];
595 FlagType save_thindraw
;
597 save_thindraw
= PCB
->Flags
;
598 CLEAR_FLAG(THINDRAWFLAG
, PCB
);
599 CLEAR_FLAG(THINDRAWPOLYFLAG
, PCB
);
600 CLEAR_FLAG(CHECKPLANESFLAG
, PCB
);
603 global
.drill_helper
= options
[HA_drillhelper
].int_value
;
604 global
.align_marks
= options
[HA_alignmarks
].int_value
;
605 global
.outline
= options
[HA_outline
].int_value
;
606 global
.mirror
= options
[HA_mirror
].int_value
;
607 global
.fillpage
= options
[HA_fillpage
].int_value
;
608 global
.automirror
= options
[HA_automirror
].int_value
;
609 global
.incolor
= options
[HA_color
].int_value
;
610 global
.bloat
= options
[HA_psbloat
].coord_value
;
611 global
.invert
= options
[HA_psinvert
].int_value
;
612 global
.fade_ratio
= CLAMP (options
[HA_psfade
].real_value
, 0, 1);
613 global
.media_idx
= options
[HA_media
].int_value
;
614 global
.media_width
= media_data
[global
.media_idx
].Width
;
615 global
.media_height
= media_data
[global
.media_idx
].Height
;
616 global
.ps_width
= global
.media_width
617 - 2.0 * media_data
[global
.media_idx
].MarginX
;
618 global
.ps_height
= global
.media_height
619 - 2.0 * media_data
[global
.media_idx
].MarginY
;
620 global
.scale_factor
= options
[HA_scale
].real_value
;
621 global
.calibration_x
= options
[HA_xcalib
].real_value
;
622 global
.calibration_y
= options
[HA_ycalib
].real_value
;
623 global
.drillcopper
= options
[HA_drillcopper
].int_value
;
624 global
.legend
= options
[HA_legend
].int_value
;
627 ps_start_file (the_file
);
632 if (PCB
->MaxWidth
> PCB
->MaxHeight
)
634 zx
= global
.ps_height
/ PCB
->MaxWidth
;
635 zy
= global
.ps_width
/ PCB
->MaxHeight
;
639 zx
= global
.ps_height
/ PCB
->MaxHeight
;
640 zy
= global
.ps_width
/ PCB
->MaxWidth
;
642 global
.scale_factor
*= MIN (zx
, zy
);
645 memset (global
.print_group
, 0, sizeof (global
.print_group
));
646 memset (global
.print_layer
, 0, sizeof (global
.print_layer
));
648 global
.outline_layer
= NULL
;
650 for (i
= 0; i
< max_copper_layer
; i
++)
652 LayerType
*layer
= PCB
->Data
->Layer
+ i
;
653 if (layer
->LineN
|| layer
->TextN
|| layer
->ArcN
|| layer
->PolygonN
)
654 global
.print_group
[GetLayerGroupNumberByNumber (i
)] = 1;
656 if (strcmp (layer
->Name
, "outline") == 0 ||
657 strcmp (layer
->Name
, "route") == 0)
659 global
.outline_layer
= layer
;
662 global
.print_group
[GetLayerGroupNumberByNumber (solder_silk_layer
)] = 1;
663 global
.print_group
[GetLayerGroupNumberByNumber (component_silk_layer
)] = 1;
664 for (i
= 0; i
< max_copper_layer
; i
++)
665 if (global
.print_group
[GetLayerGroupNumberByNumber (i
)])
666 global
.print_layer
[i
] = 1;
668 memcpy (saved_layer_stack
, LayerStack
, sizeof (LayerStack
));
669 qsort (LayerStack
, max_copper_layer
, sizeof (LayerStack
[0]), layer_sort
);
671 global
.linewidth
= -1;
672 /* reset static vars */
673 ps_set_layer (NULL
, 0, -1);
676 global
.region
.X1
= 0;
677 global
.region
.Y1
= 0;
678 global
.region
.X2
= PCB
->MaxWidth
;
679 global
.region
.Y2
= PCB
->MaxHeight
;
681 if (!global
.multi_file
)
683 /* %%Page DSC requires both a label and an ordinal */
684 fprintf (the_file
, "%%%%Page: TableOfContents 1\n");
685 fprintf (the_file
, "/Times-Roman findfont 24 scalefont setfont\n");
686 fprintf (the_file
, "/rightshow { /s exch def s stringwidth pop -1 mul 0 rmoveto s show } def\n");
687 fprintf (the_file
, "/y 72 9 mul def /toc { 100 y moveto show /y y 24 sub def } bind def\n");
688 fprintf (the_file
, "/tocp { /y y 12 sub def 90 y moveto rightshow } bind def\n");
690 global
.doing_toc
= 1;
691 global
.pagecount
= 1; /* 'pagecount' is modified by hid_expose_callback() call */
692 hid_expose_callback (&ps_hid
, &global
.region
, 0);
695 global
.pagecount
= 1; /* Reset 'pagecount' if single file */
696 global
.doing_toc
= 0;
697 ps_set_layer (NULL
, 0, -1); /* reset static vars */
698 hid_expose_callback (&ps_hid
, &global
.region
, 0);
701 fprintf (the_file
, "showpage\n");
703 memcpy (LayerStack
, saved_layer_stack
, sizeof (LayerStack
));
704 PCB
->Flags
= save_thindraw
;
708 ps_do_export (HID_Attr_Val
* options
)
711 int save_ons
[MAX_LAYER
+ 2];
716 ps_get_export_options (0);
717 for (i
= 0; i
< NUM_OPTIONS
; i
++)
718 global
.ps_values
[i
] = ps_attribute_list
[i
].default_val
;
719 options
= global
.ps_values
;
722 global
.filename
= options
[HA_psfile
].str_value
;
723 if (!global
.filename
)
724 global
.filename
= "pcb-out.ps";
726 global
.multi_file
= options
[HA_multifile
].int_value
;
728 if (global
.multi_file
)
732 fh
= psopen (global
.filename
, "toc");
735 perror (global
.filename
);
740 hid_save_and_show_layer_ons (save_ons
);
741 ps_hid_export_to_file (fh
, options
);
742 hid_restore_layer_ons (save_ons
);
744 global
.multi_file
= 0;
753 ps_parse_arguments (int *argc
, char ***argv
)
755 hid_register_attributes (ps_attribute_list
, NUM_OPTIONS
);
756 hid_parse_command_line (argc
, argv
);
760 corner (FILE *fh
, Coord x
, Coord y
, int dx
, int dy
)
762 Coord len
= MIL_TO_COORD (2000);
763 Coord len2
= MIL_TO_COORD (200);
766 * Originally 'thick' used thicker lines. Currently is uses
767 * Postscript's "device thin" line - i.e. zero width means one
768 * device pixel. The code remains in case you want to make them
769 * thicker - it needs to offset everything so that the *edge* of the
770 * thick line lines up with the edge of the board, not the *center*
774 pcb_fprintf (fh
, "gsave %mi setlinewidth %mi %mi translate %d %d scale\n",
775 thick
* 2, x
, y
, dx
, dy
);
776 pcb_fprintf (fh
, "%mi %mi moveto %mi %mi %mi 0 90 arc %mi %mi lineto\n",
777 len
, thick
, thick
, thick
, len2
+ thick
, thick
, len
);
778 if (dx
< 0 && dy
< 0)
779 pcb_fprintf (fh
, "%mi %mi moveto 0 %mi rlineto\n", len2
* 2 + thick
, thick
, -len2
);
780 fprintf (fh
, "stroke grestore\n");
784 ps_set_layer (const char *name
, int group
, int empty
)
786 static int lastgroup
= -1;
788 int idx
= (group
>= 0 && group
< max_group
)
789 ? PCB
->LayerGroups
.Entries
[group
][0]
792 name
= PCB
->Data
->Layer
[idx
].Name
;
799 if (idx
>= 0 && idx
< max_copper_layer
&& !global
.print_layer
[idx
])
802 if (strcmp (name
, "invisible") == 0)
805 global
.is_drill
= (SL_TYPE (idx
) == SL_PDRILL
|| SL_TYPE (idx
) == SL_UDRILL
);
806 global
.is_mask
= (SL_TYPE (idx
) == SL_MASK
);
807 global
.is_assy
= (SL_TYPE (idx
) == SL_ASSY
);
808 global
.is_copper
= (SL_TYPE (idx
) == 0);
809 global
.is_paste
= (SL_TYPE (idx
) == SL_PASTE
);
811 printf ("Layer %s group %d drill %d mask %d\n", name
, group
, global
.is_drill
,
815 if (global
.doing_toc
)
817 if (group
< 0 || group
!= lastgroup
)
819 if (global
.pagecount
== 1)
821 currenttime
= time (NULL
);
822 fprintf (global
.f
, "30 30 moveto (%s) show\n", PCB
->Filename
);
824 fprintf (global
.f
, "(%d.) tocp\n", global
.pagecount
);
825 fprintf (global
.f
, "(Table of Contents \\(This Page\\)) toc\n" );
827 fprintf (global
.f
, "(Created on %s) toc\n", asctime (localtime (¤ttime
)));
828 fprintf (global
.f
, "( ) tocp\n" );
833 fprintf (global
.f
, "(%d.) tocp\n", global
.pagecount
);
835 fprintf (global
.f
, "(%s) toc\n", name
);
839 if (group
< 0 || group
!= lastgroup
)
845 if (global
.pagecount
!= 0)
847 pcb_fprintf (global
.f
, "showpage\n");
850 if (global
.multi_file
)
854 ps_end_file (global
.f
);
857 global
.f
= psopen (global
.filename
, layer_type_to_file_name (idx
, FNS_fixed
));
860 perror (global
.filename
);
864 ps_start_file (global
.f
);
868 * %%Page DSC comment marks the beginning of the PostScript
869 * language instructions that describe a particular
870 * page. %%Page: requires two arguments: a page label and a
871 * sequential page number. The label may be anything, but the
872 * ordinal page number must reflect the position of that page in
873 * the body of the PostScript file and must start with 1, not 0.
875 fprintf (global
.f
, "%%%%Page: %s %d\n", layer_type_to_file_name(idx
, FNS_fixed
), global
.pagecount
);
878 mirror_this
= !mirror_this
;
879 if (global
.automirror
881 ((idx
>= 0 && group
== GetLayerGroupNumberByNumber (solder_silk_layer
))
882 || (idx
< 0 && SL_SIDE (idx
) == SL_BOTTOM_SIDE
)))
883 mirror_this
= !mirror_this
;
885 fprintf (global
.f
, "/Helvetica findfont 10 scalefont setfont\n");
888 fprintf (global
.f
, "30 30 moveto (%s) show\n", PCB
->Filename
);
890 fprintf (global
.f
, "30 41 moveto (%s, %s) show\n",
891 PCB
->Name
, layer_type_to_file_name (idx
, FNS_fixed
));
893 fprintf (global
.f
, "30 41 moveto (%s) show\n",
894 layer_type_to_file_name (idx
, FNS_fixed
));
896 fprintf (global
.f
, "( \\(mirrored\\)) show\n");
899 fprintf (global
.f
, "(, not to scale) show\n");
901 fprintf (global
.f
, "(, scale = 1:%.3f) show\n", global
.scale_factor
);
903 fprintf (global
.f
, "newpath\n");
905 pcb_fprintf (global
.f
, "72 72 scale %mi %mi translate\n",
906 global
.media_width
/ 2, global
.media_height
/ 2);
908 boffset
= global
.media_height
/ 2;
909 if (PCB
->MaxWidth
> PCB
->MaxHeight
)
911 fprintf (global
.f
, "90 rotate\n");
912 boffset
= global
.media_width
/ 2;
913 fprintf (global
.f
, "%g %g scale %% calibration\n", global
.calibration_y
, global
.calibration_x
);
916 fprintf (global
.f
, "%g %g scale %% calibration\n", global
.calibration_x
, global
.calibration_y
);
919 fprintf (global
.f
, "1 -1 scale\n");
921 fprintf (global
.f
, "%g dup neg scale\n",
922 (SL_TYPE (idx
) == SL_FAB
) ? 1.0 : global
.scale_factor
);
923 pcb_fprintf (global
.f
, "%mi %mi translate\n", -PCB
->MaxWidth
/ 2, -PCB
->MaxHeight
/ 2);
925 /* Keep the drill list from falling off the left edge of the paper,
926 * even if it means some of the board falls off the right edge.
927 * If users don't want to make smaller boards, or use fewer drill
928 * sizes, they can always ignore this sheet. */
929 if (SL_TYPE (idx
) == SL_FAB
) {
930 Coord natural
= boffset
- MIL_TO_COORD(500) - PCB
->MaxHeight
/ 2;
931 Coord needed
= PrintFab_overhang ();
932 pcb_fprintf (global
.f
, "%% PrintFab overhang natural %mi, needed %mi\n", natural
, needed
);
933 if (needed
> natural
)
934 pcb_fprintf (global
.f
, "0 %mi translate\n", needed
- natural
);
939 fprintf (global
.f
, "/gray { 1 exch sub setgray } bind def\n");
941 "/rgb { 1 1 3 { pop 1 exch sub 3 1 roll } for setrgbcolor } bind def\n");
945 fprintf (global
.f
, "/gray { setgray } bind def\n");
946 fprintf (global
.f
, "/rgb { setrgbcolor } bind def\n");
949 if ((global
.outline
&& !global
.outline_layer
) || global
.invert
)
951 pcb_fprintf (global
.f
,
952 "0 setgray 0 setlinewidth 0 0 moveto 0 "
953 "%mi lineto %mi %mi lineto %mi 0 lineto closepath %s\n",
954 PCB
->MaxHeight
, PCB
->MaxWidth
, PCB
->MaxHeight
, PCB
->MaxWidth
,
955 global
.invert
? "fill" : "stroke");
958 if (global
.align_marks
)
960 corner (global
.f
, 0, 0, -1, -1);
961 corner (global
.f
, PCB
->MaxWidth
, 0, 1, -1);
962 corner (global
.f
, PCB
->MaxWidth
, PCB
->MaxHeight
, 1, 1);
963 corner (global
.f
, 0, PCB
->MaxHeight
, -1, 1);
966 global
.linewidth
= -1;
967 use_gc (NULL
); /* reset static vars */
971 "/ty ts neg def /tx 0 def /Helvetica findfont ts scalefont setfont\n"
972 "/t { moveto lineto stroke } bind def\n"
973 "/dr { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
974 " x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath stroke } bind def\n"
975 "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
976 " x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n"
977 "/c { 0 360 arc fill } bind def\n"
978 "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
979 if (global
.drill_helper
)
980 pcb_fprintf (global
.f
,
981 "/dh { gsave %mi setlinewidth 0 gray %mi 0 360 arc stroke grestore} bind def\n",
982 (Coord
) MIN_PINORVIAHOLE
, (Coord
) (MIN_PINORVIAHOLE
* 3 / 2));
985 /* Try to outsmart ps2pdf's heuristics for page rotation, by putting
986 * text on all pages -- even if that text is blank */
987 if (SL_TYPE (idx
) != SL_FAB
)
989 "gsave tx ty translate 1 -1 scale 0 0 moveto (Layer %s) show grestore newpath /ty ty ts sub def\n",
992 fprintf (global
.f
, "gsave tx ty translate 1 -1 scale 0 0 moveto ( ) show grestore newpath /ty ty ts sub def\n");
995 /* If we're printing a copper layer other than the outline layer,
996 and we want to "print outlines", and we have an outline layer,
997 print the outline layer on this layer also. */
998 if (global
.outline
&&
1000 global
.outline_layer
!= NULL
&&
1001 global
.outline_layer
!= PCB
->Data
->Layer
+idx
&&
1002 strcmp (name
, "outline") != 0 &&
1003 strcmp (name
, "route") != 0
1006 DrawLayer (global
.outline_layer
, &global
.region
);
1015 hidGC rv
= (hidGC
) calloc (1, sizeof (hid_gc_struct
));
1016 rv
->me_pointer
= &ps_hid
;
1017 rv
->cap
= Trace_Cap
;
1022 ps_destroy_gc (hidGC gc
)
1028 ps_use_mask (int use_it
)
1034 ps_set_color (hidGC gc
, const char *name
)
1036 if (strcmp (name
, "erase") == 0 || strcmp (name
, "drill") == 0)
1038 gc
->r
= gc
->g
= gc
->b
= 255;
1041 else if (global
.incolor
)
1044 sscanf (name
+ 1, "%02x%02x%02x", &r
, &g
, &b
);
1052 gc
->r
= gc
->g
= gc
->b
= 0;
1058 ps_set_line_cap (hidGC gc
, EndCapStyle style
)
1064 ps_set_line_width (hidGC gc
, Coord width
)
1070 ps_set_draw_xor (hidGC gc
, int xor_
)
1076 ps_set_draw_faded (hidGC gc
, int faded
)
1084 static int lastcap
= -1;
1085 static int lastcolor
= -1;
1089 lastcap
= lastcolor
= -1;
1092 if (gc
->me_pointer
!= &ps_hid
)
1094 fprintf (stderr
, "Fatal: GC from another HID passed to ps HID\n");
1097 if (global
.linewidth
!= gc
->width
)
1099 pcb_fprintf (global
.f
, "%mi setlinewidth\n",
1100 gc
->width
+ (gc
->erase
? -2 : 2) * global
.bloat
);
1101 global
.linewidth
= gc
->width
;
1103 if (lastcap
!= gc
->cap
)
1117 fprintf (global
.f
, "%d setlinecap %d setlinejoin\n", c
, c
);
1120 #define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
1121 if (lastcolor
!= CBLEND (gc
))
1123 if (global
.is_drill
|| global
.is_mask
)
1125 fprintf (global
.f
, "%d gray\n", gc
->erase
? 0 : 1);
1136 r
= (1 - global
.fade_ratio
) * 255 + global
.fade_ratio
* r
;
1137 g
= (1 - global
.fade_ratio
) * 255 + global
.fade_ratio
* g
;
1138 b
= (1 - global
.fade_ratio
) * 255 + global
.fade_ratio
* b
;
1140 if (gc
->r
== gc
->g
&& gc
->g
== gc
->b
)
1141 fprintf (global
.f
, "%g gray\n", r
/ 255.0);
1143 fprintf (global
.f
, "%g %g %g rgb\n", r
/ 255.0, g
/ 255.0, b
/ 255.0);
1144 lastcolor
= CBLEND (gc
);
1150 ps_draw_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1153 pcb_fprintf (global
.f
, "%mi %mi %mi %mi dr\n", x1
, y1
, x2
, y2
);
1156 static void ps_fill_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
);
1157 static void ps_fill_circle (hidGC gc
, Coord cx
, Coord cy
, Coord radius
);
1160 ps_draw_line (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1163 /* If you're etching your own paste mask, this will reduce the
1164 amount of brass you need to etch by drawing outlines for large
1165 pads. See also ps_fill_rect. */
1166 if (is_paste
&& gc
->width
> 2500 && gc
->cap
== Square_Cap
1167 && (x1
== x2
|| y1
== y2
))
1171 { t
= x1
; x1
= x2
; x2
= t
; }
1173 { t
= y1
; y1
= y2
; y2
= t
; }
1175 ps_fill_rect (gc
, x1
-w
, y1
-w
, x2
+w
, y2
+w
);
1179 if (x1
== x2
&& y1
== y2
)
1181 Coord w
= gc
->width
/ 2;
1182 if (gc
->cap
== Square_Cap
)
1183 ps_fill_rect (gc
, x1
- w
, y1
- w
, x1
+ w
, y1
+ w
);
1185 ps_fill_circle (gc
, x1
, y1
, w
);
1189 pcb_fprintf (global
.f
, "%mi %mi %mi %mi t\n", x1
, y1
, x2
, y2
);
1193 ps_draw_arc (hidGC gc
, Coord cx
, Coord cy
, Coord width
, Coord height
,
1194 Angle start_angle
, Angle delta_angle
)
1197 if (delta_angle
> 0)
1200 ea
= start_angle
+ delta_angle
;
1204 sa
= start_angle
+ delta_angle
;
1208 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1209 cx
, cy
, width
, height
, start_angle
, delta_angle
, sa
, ea
);
1212 pcb_fprintf (global
.f
, "%ma %ma %mi %mi %mi %mi %g a\n",
1213 sa
, ea
, -width
, height
, cx
, cy
,
1214 (double) (global
.linewidth
+ 2 * global
.bloat
) / (double) width
);
1218 ps_fill_circle (hidGC gc
, Coord cx
, Coord cy
, Coord radius
)
1221 if (!gc
->erase
|| !global
.is_copper
|| global
.drillcopper
)
1223 if (gc
->erase
&& global
.is_copper
&& global
.drill_helper
1224 && radius
>= PCB
->minDrill
/ 4)
1225 radius
= PCB
->minDrill
/ 4;
1226 pcb_fprintf (global
.f
, "%mi %mi %mi c\n",
1227 cx
, cy
, radius
+ (gc
->erase
? -1 : 1) * global
.bloat
);
1232 ps_fill_polygon (hidGC gc
, int n_coords
, Coord
*x
, Coord
*y
)
1235 char *op
= "moveto";
1237 for (i
= 0; i
< n_coords
; i
++)
1239 pcb_fprintf (global
.f
, "%mi %mi %s\n", x
[i
], y
[i
], op
);
1242 fprintf (global
.f
, "fill\n");
1246 ps_fill_pcb_polygon (hidGC gc
, PolygonType
* poly
, const BoxType
* clip_box
)
1248 /* Ignore clip_box, just draw everything */
1256 pl
= poly
->Clipped
->contours
;
1264 pcb_fprintf (global
.f
, "%mi %mi %s\n", v
->point
[0], v
->point
[1], op
);
1267 while ((v
= v
->next
) != pl
->head
.next
);
1269 while ((pl
= pl
->next
) != NULL
);
1271 fprintf (global
.f
, "fill\n");
1275 ps_fill_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1291 /* See comment in ps_draw_line. */
1292 if (is_paste
&& (x2
-x1
)>2500 && (y2
-y1
)>2500)
1295 lastcap
= Round_Cap
;
1296 fprintf(f
, "1000 setlinewidth 1 setlinecap 1 setlinejoin\n");
1297 fprintf(f
, "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
1298 x1
+500-bloat
, y1
+500-bloat
,
1299 x1
+500-bloat
, y2
-500+bloat
,
1300 x2
-500+bloat
, y2
-500+bloat
,
1301 x2
-500+bloat
, y1
+500-bloat
);
1305 pcb_fprintf (global
.f
, "%mi %mi %mi %mi r\n",
1306 x1
- global
.bloat
, y1
- global
.bloat
,
1307 x2
+ global
.bloat
, y2
+ global
.bloat
);
1310 HID_Attribute ps_calib_attribute_list
[] = {
1311 {"lprcommand", "Command to print",
1312 HID_String
, 0, 0, {0, 0, 0}, 0, 0},
1315 static const char * const calib_lines
[] = {
1317 "%%Title: Calibration Page\n",
1318 "%%PageOrder: Ascend\n",
1322 "%%Page: Calibrate 1\n",
1326 "0.375 0.375 moveto\n",
1327 "8.125 0.375 lineto\n",
1328 "8.125 10.625 lineto\n",
1329 "0.375 10.625 lineto\n",
1330 "closepath stroke\n",
1332 "0.5 0.5 translate\n",
1333 "0.001 setlinewidth\n",
1335 "/Times-Roman findfont 0.2 scalefont setfont\n",
1338 " 0 lt { -1 } { 1 } ifelse\n",
1342 " /units exch def\n",
1346 " /x x sign 0.5 mul def\n",
1348 " 0 setlinewidth\n",
1349 " newpath x y 0.25 0 180 arc gsave 0.85 setgray fill grestore closepath stroke\n",
1350 " newpath x 0 0.25 180 360 arc gsave 0.85 setgray fill grestore closepath stroke\n",
1351 " 0.001 setlinewidth\n",
1355 "% -0.07 -0.2 rlineto 0.14 0 rmoveto -0.07 0.2 rlineto\n",
1357 " -0.1 0 rlineto 0.2 0 rlineto\n",
1360 "% -0.07 0.2 rlineto 0.14 0 rmoveto -0.07 -0.2 rlineto\n",
1362 " -0.1 0 rlineto 0.2 0 rlineto\n",
1366 " y 0.2 sub moveto\n",
1373 " 1.5 y moveto str show\n",
1374 " /y y 0.25 sub def\n",
1377 "(Please measure ONE of the horizontal lines, in the units indicated for)t\n",
1378 "(that line, and enter that value as X. Similarly, measure ONE of the)t\n",
1379 "(vertical lines and enter that value as Y. Measurements should be)t\n",
1380 "(between the flat faces of the semicircles.)t\n",
1382 "(The large box is 10.25 by 7.75 inches)t\n",
1384 "/in { } bind def\n",
1385 "/cm { 2.54 div } bind def\n",
1386 "/mm { 25.4 div } bind def\n",
1392 guess(double val
, double close_to
, double *calib
)
1394 if (val
>= close_to
* 0.9
1395 && val
<= close_to
* 1.1)
1397 *calib
= close_to
/ val
;
1404 ps_calibrate_1 (double xval
, double yval
, int use_command
)
1406 HID_Attr_Val vals
[3];
1408 int used_popen
= 0, c
;
1410 if (xval
> 0 && yval
> 0)
1412 if (guess (xval
, 4, &global
.calibration_x
))
1413 if (guess (xval
, 15, &global
.calibration_x
))
1414 if (guess (xval
, 7.5, &global
.calibration_x
))
1417 ps_attribute_list
[HA_xcalib
].default_val
.real_value
=
1418 global
.calibration_x
= xval
;
1420 Message("X value of %g is too far off.\n"
1421 "Expecting it near: 1.0, 4.0, 15.0, 7.5\n", xval
);
1423 if (guess (yval
, 4, &global
.calibration_y
))
1424 if (guess (yval
, 20, &global
.calibration_y
))
1425 if (guess (yval
, 10, &global
.calibration_y
))
1428 ps_attribute_list
[HA_ycalib
].default_val
.real_value
=
1429 global
.calibration_y
= yval
;
1431 Message("Y value of %g is too far off.\n"
1432 "Expecting it near: 1.0, 4.0, 20.0, 10.0\n", yval
);
1437 if (ps_calib_attribute_list
[0].default_val
.str_value
== NULL
)
1439 ps_calib_attribute_list
[0].default_val
.str_value
= strdup ("lpr");
1442 if (gui
->attribute_dialog (ps_calib_attribute_list
, 1, vals
, _("Print Calibration Page"), _("Generates a printer calibration page")))
1445 if (use_command
|| strchr (vals
[0].str_value
, '|'))
1447 const char *cmd
= vals
[0].str_value
;
1448 while (*cmd
== ' ' || *cmd
== '|')
1450 ps_cal_file
= popen (cmd
, "w");
1454 ps_cal_file
= fopen (vals
[0].str_value
, "w");
1456 for (c
=0; calib_lines
[c
]; c
++)
1457 fputs(calib_lines
[c
], ps_cal_file
);
1459 fprintf (ps_cal_file
, "4 in 0.5 (Y in) cbar\n");
1460 fprintf (ps_cal_file
, "20 cm 1.5 (Y cm) cbar\n");
1461 fprintf (ps_cal_file
, "10 in 2.5 (Y in) cbar\n");
1462 fprintf (ps_cal_file
, "-90 rotate\n");
1463 fprintf (ps_cal_file
, "4 in -0.5 (X in) cbar\n");
1464 fprintf (ps_cal_file
, "15 cm -1.5 (X cm) cbar\n");
1465 fprintf (ps_cal_file
, "7.5 in -2.5 (X in) cbar\n");
1467 fprintf (ps_cal_file
, "showpage\n");
1469 fprintf (ps_cal_file
, "%%%%EOF\n");
1472 pclose (ps_cal_file
);
1474 fclose (ps_cal_file
);
1478 ps_calibrate (double xval
, double yval
)
1480 ps_calibrate_1 (xval
, yval
, 0);
1484 ps_set_crosshair (int x
, int y
, int action
)
1488 #include "dolists.h"
1492 void ps_ps_init (HID
*hid
)
1494 hid
->get_export_options
= ps_get_export_options
;
1495 hid
->do_export
= ps_do_export
;
1496 hid
->parse_arguments
= ps_parse_arguments
;
1497 hid
->set_layer
= ps_set_layer
;
1498 hid
->make_gc
= ps_make_gc
;
1499 hid
->destroy_gc
= ps_destroy_gc
;
1500 hid
->use_mask
= ps_use_mask
;
1501 hid
->set_color
= ps_set_color
;
1502 hid
->set_line_cap
= ps_set_line_cap
;
1503 hid
->set_line_width
= ps_set_line_width
;
1504 hid
->set_draw_xor
= ps_set_draw_xor
;
1505 hid
->set_draw_faded
= ps_set_draw_faded
;
1506 hid
->draw_line
= ps_draw_line
;
1507 hid
->draw_arc
= ps_draw_arc
;
1508 hid
->draw_rect
= ps_draw_rect
;
1509 hid
->fill_circle
= ps_fill_circle
;
1510 hid
->fill_polygon
= ps_fill_polygon
;
1511 hid
->fill_pcb_polygon
= ps_fill_pcb_polygon
;
1512 hid
->fill_rect
= ps_fill_rect
;
1513 hid
->calibrate
= ps_calibrate
;
1514 hid
->set_crosshair
= ps_set_crosshair
;
1520 memset (&ps_hid
, 0, sizeof (HID
));
1522 common_nogui_init (&ps_hid
);
1523 common_draw_helpers_init (&ps_hid
);
1524 ps_ps_init (&ps_hid
);
1526 ps_hid
.struct_size
= sizeof (HID
);
1528 ps_hid
.description
= "Postscript export";
1529 ps_hid
.exporter
= 1;
1530 ps_hid
.poly_before
= 1;
1532 hid_register_hid (&ps_hid
);
1535 #include "ps_lists.h"