1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
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>
11 
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"
18 
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"
27 
28 #ifdef HAVE_LIBDMALLOC
29 #include <dmalloc.h>
30 #endif
31 
32 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PS function %s.\n", __FUNCTION__); abort()
33 
34 static int ps_set_layer (const char *name, int group, int empty);
35 static void use_gc (hidGC gc);
36 
37 typedef struct hid_gc_struct
38 {
39   HID *me_pointer;
40   EndCapStyle cap;
41   Coord width;
42   unsigned char r, g, b;
43   int erase;
44   int faded;
45 } hid_gc_struct;
46 
47 static const char *medias[] = {
48   "A0", "A1", "A2", "A3", "A4", "A5",
49   "A6", "A7", "A8", "A9", "A10",
50   "B0", "B1", "B2", "B3", "B4", "B5",
51   "B6", "B7", "B8", "B9", "B10",
52   "Letter", "11x17", "Ledger",
53   "Legal", "Executive",
54   "A-Size", "B-size",
55   "C-Size", "D-size", "E-size",
56   "US-Business_Card", "Intl-Business_Card",
57   0
58 };
59 
60 typedef struct
61 {
62   char *name;
63   Coord Width, Height;
64   Coord MarginX, MarginY;
65 } MediaType;
66 
67 /*
68  * Metric ISO sizes in mm.  See http://en.wikipedia.org/wiki/ISO_paper_sizes
69  *
70  * A0  841 x 1189
71  * A1  594 x 841
72  * A2  420 x 594
73  * A3  297 x 420
74  * A4  210 x 297
75  * A5  148 x 210
76  * A6  105 x 148
77  * A7   74 x 105
78  * A8   52 x  74
79  * A9   37 x  52
80  * A10  26 x  37
81  *
82  * B0  1000 x 1414
83  * B1   707 x 1000
84  * B2   500 x  707
85  * B3   353 x  500
86  * B4   250 x  353
87  * B5   176 x  250
88  * B6   125 x  176
89  * B7    88 x  125
90  * B8    62 x   88
91  * B9    44 x   62
92  * B10   31 x   44
93  *
94  * awk '{printf("  {\"%s\", %d, %d, MARGINX, MARGINY},\n", $2, $3*100000/25.4, $5*100000/25.4)}'
95  *
96  * See http://en.wikipedia.org/wiki/Paper_size#Loose_sizes for some of the other sizes.  The
97  * {A,B,C,D,E}-Size here are the ANSI sizes and not the architectural sizes.
98  */
99 
100 #define MARGINX MIL_TO_COORD(500)
101 #define MARGINY MIL_TO_COORD(500)
102 
103 static MediaType media_data[] = {
104   {"A0", MM_TO_COORD(841), MM_TO_COORD(1189), MARGINX, MARGINY},
105   {"A1", MM_TO_COORD(594), MM_TO_COORD(841), MARGINX, MARGINY},
106   {"A2", MM_TO_COORD(420), MM_TO_COORD(594), MARGINX, MARGINY},
107   {"A3", MM_TO_COORD(297), MM_TO_COORD(420), MARGINX, MARGINY},
108   {"A4", MM_TO_COORD(210), MM_TO_COORD(297), MARGINX, MARGINY},
109   {"A5", MM_TO_COORD(148), MM_TO_COORD(210), MARGINX, MARGINY},
110   {"A6", MM_TO_COORD(105), MM_TO_COORD(148), MARGINX, MARGINY},
111   {"A7", MM_TO_COORD(74), MM_TO_COORD(105), MARGINX, MARGINY},
112   {"A8", MM_TO_COORD(52), MM_TO_COORD(74), MARGINX, MARGINY},
113   {"A9", MM_TO_COORD(37), MM_TO_COORD(52), MARGINX, MARGINY},
114   {"A10", MM_TO_COORD(26), MM_TO_COORD(37), MARGINX, MARGINY},
115   {"B0", MM_TO_COORD(1000), MM_TO_COORD(1414), MARGINX, MARGINY},
116   {"B1", MM_TO_COORD(707), MM_TO_COORD(1000), MARGINX, MARGINY},
117   {"B2", MM_TO_COORD(500), MM_TO_COORD(707), MARGINX, MARGINY},
118   {"B3", MM_TO_COORD(353), MM_TO_COORD(500), MARGINX, MARGINY},
119   {"B4", MM_TO_COORD(250), MM_TO_COORD(353), MARGINX, MARGINY},
120   {"B5", MM_TO_COORD(176), MM_TO_COORD(250), MARGINX, MARGINY},
121   {"B6", MM_TO_COORD(125), MM_TO_COORD(176), MARGINX, MARGINY},
122   {"B7", MM_TO_COORD(88), MM_TO_COORD(125), MARGINX, MARGINY},
123   {"B8", MM_TO_COORD(62), MM_TO_COORD(88), MARGINX, MARGINY},
124   {"B9", MM_TO_COORD(44), MM_TO_COORD(62), MARGINX, MARGINY},
125   {"B10", MM_TO_COORD(31), MM_TO_COORD(44), MARGINX, MARGINY},
126   {"Letter", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
127   {"11x17", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
128   {"Ledger", INCH_TO_COORD(17), INCH_TO_COORD(11), MARGINX, MARGINY},
129   {"Legal", INCH_TO_COORD(8.5), INCH_TO_COORD(14), MARGINX, MARGINY},
130   {"Executive", INCH_TO_COORD(7.5), INCH_TO_COORD(10), MARGINX, MARGINY},
131   {"A-size",  INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
132   {"B-size", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
133   {"C-size", INCH_TO_COORD(17), INCH_TO_COORD(22), MARGINX, MARGINY},
134   {"D-size", INCH_TO_COORD(22), INCH_TO_COORD(34), MARGINX, MARGINY},
135   {"E-size", INCH_TO_COORD(34), INCH_TO_COORD(44), MARGINX, MARGINY},
136   {"US-Business_Card", INCH_TO_COORD(3.5), INCH_TO_COORD(2.0), 0, 0},
137   {"Intl-Business_Card", INCH_TO_COORD(3.375), INCH_TO_COORD(2.125), 0, 0}
138 };
139 
140 #undef MARGINX
141 #undef MARGINY
142 
143 HID_Attribute ps_attribute_list[] = {
144   /* other HIDs expect this to be first.  */
145 
146 /* %start-doc options "91 Postscript Export"
147 @ftable @code
148 @item --psfile <string>
149 Name of the postscript output file. Can contain a path.
150 @end ftable
151 %end-doc
152 */
153   {N_("psfile"), N_("Postscript output file"),
154    HID_String, 0, 0, {0, 0, 0}, 0, 0},
155 #define HA_psfile 0
156 
157 /* %start-doc options "91 Postscript Export"
158 @ftable @code
159 @cindex drill-helper
160 @item --drill-helper
161 Print a centering target in large drill holes.
162 @end ftable
163 %end-doc
164 */
165   {N_("drill-helper"), N_("Print a centering target in large drill holes"),
166    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
167 #define HA_drillhelper 1
168 
169 /* %start-doc options "91 Postscript Export"
170 @ftable @code
171 @cindex align-marks
172 @item --align-marks
173 Print alignment marks on each sheet. This is meant to ease alignment during exposure.
174 @end ftable
175 %end-doc
176 */
177   {N_("align-marks"), N_("Print alignment marks on each sheet"),
178    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
179 #define HA_alignmarks 2
180 
181 /* %start-doc options "91 Postscript Export"
182 @ftable @code
183 @item --outline
184 Print the contents of the outline layer on each sheet.
185 @end ftable
186 %end-doc
187 */
188   {N_("outline"), N_("Print outline on each sheet"),
189    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
190 #define HA_outline 3
191 /* %start-doc options "91 Postscript Export"
192 @ftable @code
193 @item --mirror
194 Print mirror image.
195 @end ftable
196 %end-doc
197 */
198   {N_("mirror"), N_("Print mirror image of every page"),
199    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
200 #define HA_mirror 4
201 
202 /* %start-doc options "91 Postscript Export"
203 @ftable @code
204 @item --fill-page
205 Scale output to make the board fit the page.
206 @end ftable
207 %end-doc
208 */
209   {N_("fill-page"), N_("Scale board to fill page"),
210    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
211 #define HA_fillpage 5
212 
213 /* %start-doc options "91 Postscript Export"
214 @ftable @code
215 @item --auto-mirror
216 Print mirror image of appropriate layers.
217 @end ftable
218 %end-doc
219 */
220   {N_("auto-mirror"), N_("Print mirror image of appropriate layers"),
221    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
222 #define HA_automirror 6
223 
224 /* %start-doc options "91 Postscript Export"
225 @ftable @code
226 @item --ps-color
227 Postscript output in color.
228 @end ftable
229 %end-doc
230 */
231   {N_("ps-color"), N_("Prints in color"),
232    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
233 #define HA_color 7
234 
235 /* %start-doc options "91 Postscript Export"
236 @ftable @code
237 @cindex ps-bloat
238 @item --ps-bloat <num>
239 Amount to add to trace/pad/pin edges.
240 @end ftable
241 %end-doc
242 */
243   {N_("ps-bloat"), N_("Amount to add to trace/pad/pin edges"),
244    HID_Coord, -MIL_TO_COORD (100), MIL_TO_COORD (100), {0, 0, 0}, 0, 0},
245 #define HA_psbloat 8
246 
247 /* %start-doc options "91 Postscript Export"
248 @ftable @code
249 @cindex ps-invert
250 @item --ps-invert
251 Draw objects as white-on-black.
252 @end ftable
253 %end-doc
254 */
255  {N_("ps-invert"), N_("Draw objects as white-on-black"),
256    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
257 #define HA_psinvert 9
258 
259 /* %start-doc options "91 Postscript Export"
260 @ftable @code
261 @item --media <media-name>
262 Size of the media, the postscript is fitted to. The parameter
263 @code{<media-name>} can be any of the standard names for paper size: @samp{A0}
264 to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
265 @samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
266 @samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
267 @samp{Intl-Business_Card}.
268 @end ftable
269 %end-doc
270 */
271   {N_("media"), N_("Media type"),
272    HID_Enum, 0, 0, {22, 0, 0}, medias, 0},
273 #define HA_media 10
274 
275 /* %start-doc options "91 Postscript Export"
276 @ftable @code
277 @cindex psfade
278 @item --psfade <num>
279 Fade amount for assembly drawings (0.0=missing, 1.0=solid).
280 @end ftable
281 %end-doc
282 */
283   {N_("psfade"),
284    N_("Fade amount for assembly drawings (0.0=missing, 1.0=solid)"),
285    HID_Real, 0, 1, {0, 0, 0.40}, 0, 0},
286 #define HA_psfade 11
287 
288 /* %start-doc options "91 Postscript Export"
289 @ftable @code
290 @item --scale <num>
291 Scale value to compensate for printer sizing errors (1.0 = full scale).
292 @end ftable
293 %end-doc
294 */
295   {N_("scale"),
296    N_("Scale value to compensate for printer sizing errors (1.0 = full scale)"),
297    HID_Real, 0.01, 4, {0, 0, 1.00}, 0, 0},
298 #define HA_scale 12
299 
300 /* %start-doc options "91 Postscript Export"
301 @ftable @code
302 @cindex multi-file
303 @item --multi-file
304 Produce multiple files, one per page, instead of a single multi page file.
305 @end ftable
306 %end-doc
307 */
308   {N_("multi-file"),
309    N_("Produce multiple files, one per page, instead of a single file"),
310    HID_Boolean, 0, 0, {0, 0, 0.40}, 0, 0},
311 #define HA_multifile 13
312 
313 /* %start-doc options "91 Postscript Export"
314 @ftable @code
315 @item --xcalib <num>
316 Paper width. Used for x-Axis calibration.
317 @end ftable
318 %end-doc
319 */
320   {N_("xcalib"), N_("Paper width. Used for x-Axis calibration"),
321    HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
322 #define HA_xcalib 14
323 
324 /* %start-doc options "91 Postscript Export"
325 @ftable @code
326 @item --ycalib <num>
327 Paper height. Used for y-Axis calibration.
328 @end ftable
329 %end-doc
330 */
331   {N_("ycalib"), N_("Paper height. Used for y-Axis calibration"),
332    HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
333 #define HA_ycalib 15
334 
335 /* %start-doc options "91 Postscript Export"
336 @ftable @code
337 @item --drill-copper
338 Draw drill holes in pins / vias, instead of leaving solid copper.
339 @end ftable
340 %end-doc
341 */
342   {N_("drill-copper"),
343    N_("Draw drill holes in pins / vias, instead of leaving solid copper"),
344    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
345 #define HA_drillcopper 16
346 
347 /* %start-doc options "91 Postscript Export"
348 @ftable @code
349 @cindex show-legend
350 @item --show-legend
351 Print file name and scale on printout.
352 @end ftable
353 %end-doc
354 */
355   {N_("show-legend"), N_("Print file name and scale on printout"),
356    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
357 #define HA_legend 17
358 };
359 
360 #define NUM_OPTIONS (sizeof(ps_attribute_list)/sizeof(ps_attribute_list[0]))
361 
362 REGISTER_ATTRIBUTES (ps_attribute_list)
363 
364 /* All file-scope data is in global struct */
365 static struct {
366   double calibration_x, calibration_y;
367 
368   FILE *f;
369   int pagecount;
370   Coord linewidth;
371   bool print_group[MAX_GROUP];
372   bool print_layer[MAX_ALL_LAYER];
373   double fade_ratio;
374   bool multi_file;
375   Coord media_width, media_height, ps_width, ps_height;
376 
377   const char *filename;
378   bool drill_helper;
379   bool align_marks;
380   bool outline;
381   bool mirror;
382   bool fillpage;
383   bool automirror;
384   bool incolor;
385   bool doing_toc;
386   Coord bloat;
387   bool invert;
388   int media_idx;
389   bool drillcopper;
390   bool legend;
391 
392   LayerType *outline_layer;
393 
394   double scale_factor;
395 
396   BoxType region;
397 
398   HID_Attr_Val ps_values[NUM_OPTIONS];
399 
400   bool is_mask;
401   bool is_drill;
402   bool is_assy;
403   bool is_copper;
404   bool is_paste;
405 } global;
406 
407 static HID_Attribute *
ps_get_export_options(int * n)408 ps_get_export_options (int *n)
409 {
410   static char *last_made_filename = 0;
411   if (PCB)
412     derive_default_filename(PCB->Filename, &ps_attribute_list[HA_psfile], ".ps", &last_made_filename);
413 
414   if (n)
415     *n = NUM_OPTIONS;
416   return ps_attribute_list;
417 }
418 
419 static int
layer_stack_sort(const void * va,const void * vb)420 layer_stack_sort (const void *va, const void *vb)
421 {
422   int a_layer = *(int *) va;
423   int b_layer = *(int *) vb;
424   int a_group = GetLayerGroupNumberByNumber (a_layer);
425   int b_group = GetLayerGroupNumberByNumber (b_layer);
426 
427   if (b_group != a_group)
428     return b_group - a_group;
429 
430   return b_layer - a_layer;
431 }
432 
433 void
ps_start_file(FILE * f)434 ps_start_file (FILE *f)
435 {
436   time_t currenttime = time( NULL );
437 
438   fprintf (f, "%%!PS-Adobe-3.0\n");
439 
440   /* Document Structuring Conventions (DCS): */
441 
442   /* Start General Header Comments: */
443 
444   /*
445    * %%Title DCS provides text title for the document that is useful
446    * for printing banner pages.
447    */
448   fprintf (f, "%%%%Title: %s\n", PCB->Filename);
449 
450   /*
451    * %%CreationDate DCS indicates the date and time the document was
452    * created. Neither the date nor time need be in any standard
453    * format. This comment is meant to be used purely for informational
454    * purposes, such as printing on banner pages.
455    */
456   fprintf (f, "%%%%CreationDate: %s", asctime (localtime (&currenttime)));
457 
458   /*
459    * %%Creator DCS indicates the document creator, usually the name of
460    * the document composition software.
461    */
462   fprintf (f, "%%%%Creator: PCB release: %s " VERSION "\n", Progname);
463 
464   /*
465    * %%Version DCS comment can be used to note the version and
466    * revision number of a document or resource. A document manager may
467    * wish to provide version control services, or allow substitution
468    * of compatible versions/revisions of a resource or document.
469    *
470    * The format should be in the form of 'procname':
471    *  <procname>::= < name> < version> < revision>
472    *  < name> ::= < text>
473    *  < version> ::= < real>
474    *  < revision> ::= < uint>
475    *
476    * If a version numbering scheme is not used, these fields should
477    * still be filled with a dummy value of 0.
478    *
479    * There is currently no code in PCB to manage this revision number.
480    *
481    */
482   fprintf (f, "%%%%Version: (PCB %s " VERSION ") 0.0 0\n", Progname );
483 
484 
485   /*
486    * %%PageOrder DCS is intended to help document managers determine
487    * the order of pages in the document file, which in turn enables a
488    * document manager optionally to reorder the pages.  'Ascend'-The
489    * pages are in ascending order for example, 1-2-3-4-5-6.
490    */
491   fprintf (f, "%%%%PageOrder: Ascend\n" );
492 
493   /*
494    * %%Pages: < numpages> | (atend) < numpages> ::= < uint> (Total
495    * %%number of pages)
496    *
497    * %%Pages DCS defines the number of virtual pages that a document
498    * will image.  (atend) defers the count until the end of the file,
499    * which is useful for dynamically generated contents.
500    */
501   fprintf (f, "%%%%Pages: (atend)\n" );
502 
503   /*
504    * %%DocumentMedia: <name> <width> <height> <weight> <color> <type>
505    *
506    * Substitute 0 or "" for N/A.  Width and height are in points
507    * (1/72").
508    *
509    * Media sizes are in PCB units
510    */
511   pcb_fprintf (f, "%%%%DocumentMedia: %s %mi %mi 0 \"\" \"\"\n",
512                media_data[global.media_idx].name,
513                72 * media_data[global.media_idx].Width,
514                72 * media_data[global.media_idx].Height);
515   pcb_fprintf (f, "%%%%DocumentPaperSizes: %s\n", media_data[global.media_idx].name);
516 
517   /* End General Header Comments. */
518 
519   /* General Body Comments go here. Currently there are none. */
520 
521   /*
522    * %%EndComments DCS indicates an explicit end to the header
523    * comments of the document.  All global DCS's must preceded
524    * this.  A blank line gives an implicit end to the comments.
525    */
526   fprintf (f, "%%%%EndComments\n\n" );
527 }
528 
529 static void
ps_end_file(FILE * f)530 ps_end_file (FILE *f)
531 {
532   /*
533    * %%Trailer DCS must only occur once at the end of the document
534    * script.  Any post-processing or cleanup should be contained in
535    * the trailer of the document, which is anything that follows the
536    * %%Trailer comment. Any of the document level structure comments
537    * that were deferred by using the (atend) convention must be
538    * mentioned in the trailer of the document after the %%Trailer
539    * comment.
540    */
541   fprintf (f, "%%%%Trailer\n" );
542 
543   /*
544    * %%Pages was deferred until the end of the document via the
545    * (atend) mentioned, in the General Header section.
546    */
547   fprintf (f, "%%%%Pages: %d\n", global.pagecount);
548 
549   /*
550    * %%EOF DCS signifies the end of the document. When the document
551    * manager sees this comment, it issues an end-of-file signal to the
552    * PostScript interpreter.  This is done so system-dependent file
553    * endings, such as Control-D and end-of-file packets, do not
554    * confuse the PostScript interpreter.
555    */
556   fprintf (f, "%%%%EOF\n" );
557 }
558 
559 static FILE *
psopen(const char * base,const char * which)560 psopen (const char *base, const char *which)
561 {
562   FILE *ps_open_file;
563   char *buf, *suff, *buf2;
564 
565   if (!global.multi_file)
566     return fopen (base, "w");
567 
568   buf = (char *)malloc (strlen (base) + strlen (which) + 5);
569 
570   suff = (char *)strrchr (base, '.');
571   if (suff)
572     {
573       strcpy (buf, base);
574       buf2 = strrchr (buf, '.');
575       sprintf(buf2, ".%s.%s", which, suff+1);
576     }
577   else
578     {
579       sprintf(buf, "%s.%s.ps", base, which);
580     }
581   printf("PS: open %s\n", buf);
582   ps_open_file = fopen(buf, "w");
583   free (buf);
584   return ps_open_file;
585 }
586 
587 /* This is used by other HIDs that use a postscript format, like lpr
588    or eps.  */
589 void
ps_hid_export_to_file(FILE * the_file,HID_Attr_Val * options)590 ps_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
591 {
592   int i;
593   static int saved_layer_stack[MAX_LAYER];
594   FlagType save_thindraw;
595 
596   save_thindraw = PCB->Flags;
597   CLEAR_FLAG(THINDRAWFLAG, PCB);
598   CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
599   CLEAR_FLAG(CHECKPLANESFLAG, PCB);
600 
601   global.f = the_file;
602   global.drill_helper = options[HA_drillhelper].int_value;
603   global.align_marks  = options[HA_alignmarks].int_value;
604   global.outline      = options[HA_outline].int_value;
605   global.mirror       = options[HA_mirror].int_value;
606   global.fillpage     = options[HA_fillpage].int_value;
607   global.automirror   = options[HA_automirror].int_value;
608   global.incolor      = options[HA_color].int_value;
609   global.bloat        = options[HA_psbloat].coord_value;
610   global.invert       = options[HA_psinvert].int_value;
611   global.fade_ratio   = CLAMP (options[HA_psfade].real_value, 0, 1);
612   global.media_idx    = options[HA_media].int_value;
613   global.media_width  = media_data[global.media_idx].Width;
614   global.media_height = media_data[global.media_idx].Height;
615   global.ps_width     = global.media_width
616                         - 2.0 * media_data[global.media_idx].MarginX;
617   global.ps_height    = global.media_height
618                         - 2.0 * media_data[global.media_idx].MarginY;
619   global.scale_factor = options[HA_scale].real_value;
620   global.calibration_x = options[HA_xcalib].real_value;
621   global.calibration_y = options[HA_ycalib].real_value;
622   global.drillcopper  = options[HA_drillcopper].int_value;
623   global.legend       = options[HA_legend].int_value;
624 
625   if (the_file)
626     ps_start_file (the_file);
627 
628   if (global.fillpage)
629     {
630       double zx, zy;
631       if (PCB->MaxWidth > PCB->MaxHeight)
632 	{
633 	  zx = global.ps_height / PCB->MaxWidth;
634 	  zy = global.ps_width  / PCB->MaxHeight;
635 	}
636       else
637 	{
638 	  zx = global.ps_height / PCB->MaxHeight;
639 	  zy = global.ps_width  / PCB->MaxWidth;
640 	}
641       global.scale_factor *= MIN (zx, zy);
642     }
643 
644   memset (global.print_group, 0, sizeof (global.print_group));
645   memset (global.print_layer, 0, sizeof (global.print_layer));
646 
647   global.outline_layer = NULL;
648 
649   for (i = 0; i < max_copper_layer; i++)
650     {
651       LayerType *layer = PCB->Data->Layer + i;
652       if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
653 	global.print_group[GetLayerGroupNumberByNumber (i)] = 1;
654 
655       if (strcmp (layer->Name, "outline") == 0 ||
656 	  strcmp (layer->Name, "route") == 0)
657 	{
658 	  global.outline_layer = layer;
659 	}
660     }
661   global.print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
662   global.print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
663   for (i = 0; i < max_copper_layer; i++)
664     if (global.print_group[GetLayerGroupNumberByNumber (i)])
665       global.print_layer[i] = 1;
666 
667   memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
668   qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
669 
670   global.linewidth = -1;
671   /* reset static vars */
672   ps_set_layer (NULL, 0, -1);
673   use_gc (NULL);
674 
675   global.region.X1 = 0;
676   global.region.Y1 = 0;
677   global.region.X2 = PCB->MaxWidth;
678   global.region.Y2 = PCB->MaxHeight;
679 
680   if (!global.multi_file)
681     {
682       /* %%Page DSC requires both a label and an ordinal */
683       fprintf (the_file, "%%%%Page: TableOfContents 1\n");
684       fprintf (the_file, "/Times-Roman findfont 14 scalefont setfont\n");
685       fprintf (the_file, "/rightshow { /s exch def s stringwidth pop -1 mul 0 rmoveto s show } def\n");
686       fprintf (the_file, "/y 72 9 mul def /toc { 100 y moveto show /y y 24 sub def } bind def\n");
687       fprintf (the_file, "/tocp { /y y 0 sub def 90 y moveto rightshow } bind def\n");
688 
689       global.doing_toc = 1;
690       global.pagecount = 1;  /* 'pagecount' is modified by hid_expose_callback() call */
691       hid_expose_callback (&ps_hid, &global.region, 0);
692     }
693 
694   global.pagecount = 1; /* Reset 'pagecount' if single file */
695   global.doing_toc = 0;
696   ps_set_layer (NULL, 0, -1);  /* reset static vars */
697   hid_expose_callback (&ps_hid, &global.region, 0);
698 
699   if (the_file)
700     fprintf (the_file, "showpage\n");
701 
702   memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
703   PCB->Flags = save_thindraw;
704 }
705 
706 static void
ps_do_export(HID_Attr_Val * options)707 ps_do_export (HID_Attr_Val * options)
708 {
709   FILE *fh;
710   int save_ons[MAX_ALL_LAYER];
711   int i;
712 
713   if (!options)
714     {
715       ps_get_export_options (0);
716       for (i = 0; i < NUM_OPTIONS; i++)
717 	global.ps_values[i] = ps_attribute_list[i].default_val;
718       options = global.ps_values;
719     }
720 
721   global.filename = options[HA_psfile].str_value;
722   if (!global.filename)
723     global.filename = "pcb-out.ps";
724 
725   global.multi_file = options[HA_multifile].int_value;
726 
727   if (global.multi_file)
728     fh = 0;
729   else
730     {
731       fh = psopen (global.filename, "toc");
732       if (!fh)
733 	{
734 	  perror (global.filename);
735 	  return;
736 	}
737     }
738 
739   hid_save_and_show_layer_ons (save_ons);
740   ps_hid_export_to_file (fh, options);
741   hid_restore_layer_ons (save_ons);
742 
743   global.multi_file = 0;
744   if (fh)
745     {
746       ps_end_file (fh);
747       fclose (fh);
748     }
749 }
750 
751 static void
ps_parse_arguments(int * argc,char *** argv)752 ps_parse_arguments (int *argc, char ***argv)
753 {
754   hid_register_attributes (ps_attribute_list, NUM_OPTIONS);
755   hid_parse_command_line (argc, argv);
756 }
757 
758 static void
corner(FILE * fh,Coord x,Coord y,int dx,int dy)759 corner (FILE *fh, Coord x, Coord y, int dx, int dy)
760 {
761   Coord len   = MIL_TO_COORD (2000);
762   Coord len2  = MIL_TO_COORD (200);
763   Coord thick = 0;
764   /*
765    * Originally 'thick' used thicker lines.  Currently is uses
766    * Postscript's "device thin" line - i.e. zero width means one
767    * device pixel.  The code remains in case you want to make them
768    * thicker - it needs to offset everything so that the *edge* of the
769    * thick line lines up with the edge of the board, not the *center*
770    * of the thick line.
771    */
772 
773   pcb_fprintf (fh, "gsave %mi setlinewidth %mi %mi translate %d %d scale\n",
774                thick * 2, x, y, dx, dy);
775   pcb_fprintf (fh, "%mi %mi moveto %mi %mi %mi 0 90 arc %mi %mi lineto\n",
776                len, thick, thick, thick, len2 + thick, thick, len);
777   if (dx < 0 && dy < 0)
778     pcb_fprintf (fh, "%mi %mi moveto 0 %mi rlineto\n", len2 * 2 + thick, thick, -len2);
779   fprintf (fh, "stroke grestore\n");
780 }
781 
782 static int
ps_set_layer(const char * name,int group,int empty)783 ps_set_layer (const char *name, int group, int empty)
784 {
785   static int lastgroup = -1;
786   time_t currenttime;
787   int idx = (group >= 0 && group < max_group)
788             ? PCB->LayerGroups.Entries[group][0]
789             : group;
790   if (name == 0)
791     name = PCB->Data->Layer[idx].Name;
792 
793   if (empty == -1)
794     lastgroup = -1;
795   if (empty)
796     return 0;
797 
798   if (idx >= 0 && idx < max_copper_layer && !global.print_layer[idx])
799     return 0;
800 
801   if (strcmp (name, "invisible") == 0)
802     return 0;
803 
804   global.is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
805   global.is_mask  = (SL_TYPE (idx) == SL_MASK);
806   global.is_assy  = (SL_TYPE (idx) == SL_ASSY);
807   global.is_copper = (SL_TYPE (idx) == 0);
808   global.is_paste  = (SL_TYPE (idx) == SL_PASTE);
809 #if 0
810   printf ("Layer %s group %d drill %d mask %d\n", name, group, global.is_drill,
811 	  global.is_mask);
812 #endif
813 
814   if (global.doing_toc)
815     {
816       if (group < 0 || group != lastgroup)
817 	{
818           if (global.pagecount == 1)
819             {
820               currenttime = time (NULL);
821               fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
822 
823               fprintf (global.f, "(%d.) tocp\n", global.pagecount);
824               fprintf (global.f, "(Table of Contents \\(This Page\\)) toc\n" );
825 
826               fprintf (global.f, "(Created on %s) toc\n", asctime (localtime (&currenttime)));
827               fprintf (global.f, "( ) tocp\n" );
828             }
829 
830 	  global.pagecount++;
831 	  lastgroup = group;
832 	  fprintf (global.f, "(%d.) tocp\n", global.pagecount);
833 	}
834       fprintf (global.f, "(%s) toc\n", name);
835       return 0;
836     }
837 
838   if (group < 0 || group != lastgroup)
839     {
840       double boffset;
841       int mirror_this = 0;
842       lastgroup = group;
843 
844       if (global.pagecount != 0)
845 	{
846 	  pcb_fprintf (global.f, "showpage\n");
847 	}
848       global.pagecount++;
849       if (global.multi_file)
850 	{
851 	  if (global.f)
852             {
853               ps_end_file (global.f);
854               fclose (global.f);
855             }
856 	  global.f = psopen (global.filename, layer_type_to_file_name_ex (idx, FNS_fixed, name));
857 	  if (!global.f)
858 	  {
859 	    perror (global.filename);
860 	    return 0;
861 	  }
862 
863 	  ps_start_file (global.f);
864 	}
865 
866       /*
867        * %%Page DSC comment marks the beginning of the PostScript
868        * language instructions that describe a particular
869        * page. %%Page: requires two arguments: a page label and a
870        * sequential page number. The label may be anything, but the
871        * ordinal page number must reflect the position of that page in
872        * the body of the PostScript file and must start with 1, not 0.
873        */
874       fprintf (global.f, "%%%%Page: %s %d\n", layer_type_to_file_name_ex (idx, FNS_fixed, name), global.pagecount);
875 
876       if (global.mirror)
877 	mirror_this = !mirror_this;
878       if (global.automirror
879 	  &&
880 	  ((idx >= 0 && group == GetLayerGroupNumberBySide (BOTTOM_SIDE))
881 	   || (idx < 0 && SL_SIDE (idx) == SL_BOTTOM_SIDE)))
882 	mirror_this = !mirror_this;
883 
884       fprintf (global.f, "/Helvetica findfont 10 scalefont setfont\n");
885       if (global.legend)
886 	{
887 	  fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
888 	  if (PCB->Name)
889 	    fprintf (global.f, "30 41 moveto (%s, %s) show\n",
890 		     PCB->Name, layer_type_to_file_name_ex (idx, FNS_fixed, name));
891 	  else
892 	    fprintf (global.f, "30 41 moveto (%s) show\n",
893 		     layer_type_to_file_name_ex (idx, FNS_fixed, name));
894 	  if (mirror_this)
895 	    fprintf (global.f, "( \\(mirrored\\)) show\n");
896 
897 	  if (global.fillpage)
898 	    fprintf (global.f, "(, not to scale) show\n");
899 	  else
900 	    fprintf (global.f, "(, scale = 1:%.3f) show\n", global.scale_factor);
901 	}
902       fprintf (global.f, "newpath\n");
903 
904       pcb_fprintf (global.f, "72 72 scale %mi %mi translate\n",
905                    global.media_width / 2, global.media_height / 2);
906 
907       boffset = global.media_height / 2;
908       if (PCB->MaxWidth > PCB->MaxHeight)
909 	{
910 	  fprintf (global.f, "90 rotate\n");
911 	  boffset = global.media_width / 2;
912 	  fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_y, global.calibration_x);
913 	}
914       else
915 	fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_x, global.calibration_y);
916 
917       if (mirror_this)
918 	fprintf (global.f, "1 -1 scale\n");
919 
920       fprintf (global.f, "%g dup neg scale\n",
921                (SL_TYPE (idx) == SL_FAB) ? 1.0 : global.scale_factor);
922       pcb_fprintf (global.f, "%mi %mi translate\n", -PCB->MaxWidth / 2, -PCB->MaxHeight / 2);
923 
924       /* Keep the drill list from falling off the left edge of the paper,
925        * even if it means some of the board falls off the right edge.
926        * If users don't want to make smaller boards, or use fewer drill
927        * sizes, they can always ignore this sheet. */
928       if (SL_TYPE (idx) == SL_FAB) {
929         Coord natural = boffset - MIL_TO_COORD(500) - PCB->MaxHeight / 2;
930 	Coord needed  = PrintFab_overhang ();
931         pcb_fprintf (global.f, "%% PrintFab overhang natural %mi, needed %mi\n", natural, needed);
932 	if (needed > natural)
933 	  pcb_fprintf (global.f, "0 %mi translate\n", needed - natural);
934       }
935 
936       if (global.invert)
937 	{
938 	  fprintf (global.f, "/gray { 1 exch sub setgray } bind def\n");
939 	  fprintf (global.f,
940                    "/rgb { 1 1 3 { pop 1 exch sub 3 1 roll } for setrgbcolor } bind def\n");
941 	}
942       else
943 	{
944 	  fprintf (global.f, "/gray { setgray } bind def\n");
945 	  fprintf (global.f, "/rgb { setrgbcolor } bind def\n");
946 	}
947 
948       if ((global.outline && !global.outline_layer) || global.invert)
949 	{
950 	  pcb_fprintf (global.f,
951                        "0 setgray 0 setlinewidth 0 0 moveto 0 "
952                        "%mi lineto %mi %mi lineto %mi 0 lineto closepath %s\n",
953                        PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight, PCB->MaxWidth,
954                        global.invert ? "fill" : "stroke");
955 	}
956 
957       if (global.align_marks)
958 	{
959 	  corner (global.f, 0, 0, -1, -1);
960 	  corner (global.f, PCB->MaxWidth, 0, 1, -1);
961 	  corner (global.f, PCB->MaxWidth, PCB->MaxHeight, 1, 1);
962 	  corner (global.f, 0, PCB->MaxHeight, -1, 1);
963 	}
964 
965       global.linewidth = -1;
966       use_gc (NULL);  /* reset static vars */
967 
968       fprintf (global.f,
969               "/ts 1 def\n"
970               "/ty ts neg def /tx 0 def /Helvetica findfont ts scalefont setfont\n"
971               "/t { moveto lineto stroke } bind def\n"
972               "/dr { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
973               "      x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath stroke } bind def\n"
974               "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
975               "     x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n"
976               "/c { 0 360 arc fill } bind def\n"
977               "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
978       if (global.drill_helper)
979 	pcb_fprintf (global.f,
980                     "/dh { gsave %mi setlinewidth 0 gray %mi 0 360 arc stroke grestore} bind def\n",
981                     (Coord) MIN_PINORVIAHOLE, (Coord) (MIN_PINORVIAHOLE * 3 / 2));
982     }
983 #if 0
984   /* Try to outsmart ps2pdf's heuristics for page rotation, by putting
985    * text on all pages -- even if that text is blank */
986   if (SL_TYPE (idx) != SL_FAB)
987     fprintf (global.f,
988 	     "gsave tx ty translate 1 -1 scale 0 0 moveto (Layer %s) show grestore newpath /ty ty ts sub def\n",
989 	     name);
990   else
991     fprintf (global.f, "gsave tx ty translate 1 -1 scale 0 0 moveto ( ) show grestore newpath /ty ty ts sub def\n");
992 #endif
993 
994   /* If we're printing a copper layer other than the outline layer,
995      and we want to "print outlines", and we have an outline layer,
996      print the outline layer on this layer also.  */
997   if (global.outline &&
998       (global.is_copper || (strcmp(name, "topassembly") == 0) || (strcmp(name, "bottomassembly") == 0)) &&
999       global.outline_layer != NULL &&
1000       global.outline_layer != PCB->Data->Layer+idx &&
1001       strcmp (name, "outline") != 0 &&
1002       strcmp (name, "route") != 0
1003       )
1004     {
1005       DrawLayer (global.outline_layer, &global.region);
1006     }
1007 
1008   return 1;
1009 }
1010 
1011 static hidGC
ps_make_gc(void)1012 ps_make_gc (void)
1013 {
1014   hidGC rv = (hidGC) calloc (1, sizeof (hid_gc_struct));
1015   rv->me_pointer = &ps_hid;
1016   rv->cap = Trace_Cap;
1017   return rv;
1018 }
1019 
1020 static void
ps_destroy_gc(hidGC gc)1021 ps_destroy_gc (hidGC gc)
1022 {
1023   free (gc);
1024 }
1025 
1026 static void
ps_use_mask(enum mask_mode mode)1027 ps_use_mask (enum mask_mode mode)
1028 {
1029   /* does nothing */
1030 }
1031 
1032 static void
ps_set_color(hidGC gc,const char * name)1033 ps_set_color (hidGC gc, const char *name)
1034 {
1035   if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
1036     {
1037       gc->r = gc->g = gc->b = 255;
1038       gc->erase = 1;
1039     }
1040   else if (global.incolor)
1041     {
1042       int r, g, b;
1043       sscanf (name + 1, "%02x%02x%02x", &r, &g, &b);
1044       gc->r = r;
1045       gc->g = g;
1046       gc->b = b;
1047       gc->erase = 0;
1048     }
1049   else
1050     {
1051       gc->r = gc->g = gc->b = 0;
1052       gc->erase = 0;
1053     }
1054 }
1055 
1056 static void
ps_set_line_cap(hidGC gc,EndCapStyle style)1057 ps_set_line_cap (hidGC gc, EndCapStyle style)
1058 {
1059   gc->cap = style;
1060 }
1061 
1062 static void
ps_set_line_width(hidGC gc,Coord width)1063 ps_set_line_width (hidGC gc, Coord width)
1064 {
1065   gc->width = width;
1066 }
1067 
1068 static void
ps_set_draw_xor(hidGC gc,int xor_)1069 ps_set_draw_xor (hidGC gc, int xor_)
1070 {
1071   ;
1072 }
1073 
1074 static void
ps_set_draw_faded(hidGC gc,int faded)1075 ps_set_draw_faded (hidGC gc, int faded)
1076 {
1077   gc->faded = faded;
1078 }
1079 
1080 static void
use_gc(hidGC gc)1081 use_gc (hidGC gc)
1082 {
1083   static int lastcap = -1;
1084   static int lastcolor = -1;
1085 
1086   if (gc == NULL)
1087     {
1088       lastcap = lastcolor = -1;
1089       return;
1090     }
1091   if (gc->me_pointer != &ps_hid)
1092     {
1093       fprintf (stderr, "Fatal: GC from another HID passed to ps HID\n");
1094       abort ();
1095     }
1096   if (global.linewidth != gc->width)
1097     {
1098       pcb_fprintf (global.f, "%mi setlinewidth\n",
1099                    gc->width + (gc->erase ? -2 : 2) * global.bloat);
1100       global.linewidth = gc->width;
1101     }
1102   if (lastcap != gc->cap)
1103     {
1104       int c;
1105       switch (gc->cap)
1106 	{
1107 	case Round_Cap:
1108 	case Trace_Cap:
1109 	  c = 1;
1110 	  break;
1111 	default:
1112 	case Square_Cap:
1113 	  c = 2;
1114 	  break;
1115 	}
1116       fprintf (global.f, "%d setlinecap %d setlinejoin\n", c, c);
1117       lastcap = gc->cap;
1118     }
1119 #define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
1120   if (lastcolor != CBLEND (gc))
1121     {
1122       if (global.is_drill || global.is_mask)
1123 	{
1124 	  fprintf (global.f, "%d gray\n", gc->erase ? 0 : 1);
1125 	  lastcolor = 0;
1126 	}
1127       else
1128 	{
1129 	  double r, g, b;
1130 	  r = gc->r;
1131 	  g = gc->g;
1132 	  b = gc->b;
1133 	  if (gc->faded)
1134 	    {
1135 	      r = (1 - global.fade_ratio) * 255 + global.fade_ratio * r;
1136 	      g = (1 - global.fade_ratio) * 255 + global.fade_ratio * g;
1137 	      b = (1 - global.fade_ratio) * 255 + global.fade_ratio * b;
1138 	    }
1139 	  if (gc->r == gc->g && gc->g == gc->b)
1140 	    fprintf (global.f, "%g gray\n", r / 255.0);
1141 	  else
1142 	    fprintf (global.f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
1143 	  lastcolor = CBLEND (gc);
1144 	}
1145     }
1146 }
1147 
1148 static void
ps_draw_rect(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2)1149 ps_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1150 {
1151   use_gc (gc);
1152   pcb_fprintf (global.f, "%mi %mi %mi %mi dr\n", x1, y1, x2, y2);
1153 }
1154 
1155 static void ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
1156 static void ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
1157 
1158 static void
ps_draw_line(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2)1159 ps_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1160 {
1161 #if 0
1162   /* If you're etching your own paste mask, this will reduce the
1163      amount of brass you need to etch by drawing outlines for large
1164      pads.  See also ps_fill_rect.  */
1165   if (is_paste && gc->width > 2500 && gc->cap == Square_Cap
1166       && (x1 == x2 || y1 == y2))
1167     {
1168       Coord t, w;
1169       if (x1 > x2)
1170 	{ t = x1; x1 = x2; x2 = t; }
1171       if (y1 > y2)
1172 	{ t = y1; y1 = y2; y2 = t; }
1173       w = gc->width/2;
1174       ps_fill_rect (gc, x1-w, y1-w, x2+w, y2+w);
1175       return;
1176     }
1177 #endif
1178   if (x1 == x2 && y1 == y2)
1179     {
1180       Coord w = gc->width / 2;
1181       if (gc->cap == Square_Cap)
1182 	ps_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
1183       else
1184 	ps_fill_circle (gc, x1, y1, w);
1185       return;
1186     }
1187   use_gc (gc);
1188   pcb_fprintf (global.f, "%mi %mi %mi %mi t\n", x1, y1, x2, y2);
1189 }
1190 
1191 static void
ps_draw_arc(hidGC gc,Coord cx,Coord cy,Coord width,Coord height,Angle start_angle,Angle delta_angle)1192 ps_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1193 	     Angle start_angle, Angle delta_angle)
1194 {
1195   Angle sa, ea;
1196   double linewidth;
1197 
1198   if (delta_angle > 0)
1199     {
1200       sa = start_angle;
1201       ea = start_angle + delta_angle;
1202     }
1203   else
1204     {
1205       sa = start_angle + delta_angle;
1206       ea = start_angle;
1207     }
1208 
1209   use_gc (gc);
1210 
1211   /* Other than pcb's screen renderer, PostScript (at least GhostScript)
1212      internally limits linewidth to (diameter / 2), so no drawing of a dot with
1213      a circle of zero diameter. Compensate for this by making diameter larger
1214      and line width thinner.
1215 
1216      Handling of this case currently has only circles in mind, no ellipses.
1217      The regular case works with ellipses, too. */
1218   if (width < global.linewidth)
1219     {
1220       Coord outer_radius;
1221 
1222       outer_radius = width + global.linewidth / 2 + global.bloat;
1223       width = height = outer_radius / 2;
1224       linewidth = 2.0;
1225     }
1226   else
1227     linewidth = (double) (global.linewidth + 2 * global.bloat) / (double) width;
1228 
1229   /* The line of PostScript written here is a bit odd; linewidth isn't
1230      absolute, but relative to circle diameter. This is neccessary for
1231      ellipses, which are drawn by streching (transforming) a circle. */
1232   pcb_fprintf (global.f, "%ma %ma %mi %mi %mi %mi %`g a\n",
1233                sa, ea, -width, height, cx, cy, linewidth);
1234 
1235 }
1236 
1237 static void
ps_fill_circle(hidGC gc,Coord cx,Coord cy,Coord radius)1238 ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1239 {
1240   use_gc (gc);
1241   if (!gc->erase || !global.is_copper || global.drillcopper)
1242     {
1243       if (gc->erase && global.is_copper && global.drill_helper
1244 	  && radius >= PCB->minDrill / 4)
1245 	radius = PCB->minDrill / 4;
1246       pcb_fprintf (global.f, "%mi %mi %mi c\n",
1247                    cx, cy, radius + (gc->erase ? -1 : 1) * global.bloat);
1248     }
1249 }
1250 
1251 static void
ps_fill_polygon(hidGC gc,int n_coords,Coord * x,Coord * y)1252 ps_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1253 {
1254   int i;
1255   char *op = "moveto";
1256   use_gc (gc);
1257   for (i = 0; i < n_coords; i++)
1258     {
1259       pcb_fprintf (global.f, "%mi %mi %s\n", x[i], y[i], op);
1260       op = "lineto";
1261     }
1262   fprintf (global.f, "fill\n");
1263 }
1264 
1265 static void
fill_polyarea(hidGC gc,POLYAREA * pa,const BoxType * clip_box)1266 fill_polyarea (hidGC gc, POLYAREA * pa, const BoxType * clip_box)
1267 {
1268   /* Ignore clip_box, just draw everything */
1269 
1270   VNODE *v;
1271   PLINE *pl;
1272   char *op;
1273 
1274   use_gc (gc);
1275 
1276   pl = pa->contours;
1277 
1278   do
1279     {
1280       v = pl->head.next;
1281       op = "moveto";
1282       do
1283 	{
1284 	  pcb_fprintf (global.f, "%mi %mi %s\n", v->point[0], v->point[1], op);
1285 	  op = "lineto";
1286 	}
1287       while ((v = v->next) != pl->head.next);
1288     }
1289   while ((pl = pl->next) != NULL);
1290 
1291   fprintf (global.f, "fill\n");
1292 }
1293 
1294 static void
ps_draw_pcb_polygon(hidGC gc,PolygonType * poly,const BoxType * clip_box)1295 ps_draw_pcb_polygon (hidGC gc, PolygonType * poly, const BoxType * clip_box)
1296 {
1297   if (!poly->Clipped)
1298     return;
1299   fill_polyarea (gc, poly->Clipped, clip_box);
1300   if (TEST_FLAG (FULLPOLYFLAG, poly))
1301     {
1302       POLYAREA *pa;
1303 
1304       for (pa = poly->Clipped->f; pa != poly->Clipped; pa = pa->f)
1305         fill_polyarea (gc, pa, clip_box);
1306     }
1307 }
1308 
1309 static void
ps_fill_rect(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2)1310 ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1311 {
1312   use_gc (gc);
1313   if (x1 > x2)
1314     {
1315       Coord t = x1;
1316       x1 = x2;
1317       x2 = t;
1318     }
1319   if (y1 > y2)
1320     {
1321       Coord t = y1;
1322       y1 = y2;
1323       y2 = t;
1324     }
1325 #if 0
1326   /* See comment in ps_draw_line.  */
1327   if (is_paste && (x2-x1)>2500 && (y2-y1)>2500)
1328     {
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;
1338     }
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);
1343 }
1344 
1345 HID_Attribute ps_calib_attribute_list[] = {
1346   {N_("lprcommand"), N_("Command to print"),
1347    HID_String, 0, 0, {0, 0, 0}, 0, 0},
1348 };
1349 
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",
1423   0
1424 };
1425 
1426 static int
guess(double val,double close_to,double * calib)1427 guess(double val, double close_to, double *calib)
1428 {
1429   if (val >= close_to * 0.9
1430       && val <= close_to * 1.1)
1431     {
1432       *calib = close_to / val;
1433       return 0;
1434     }
1435   return 1;
1436 }
1437 
1438 void
ps_calibrate_1(double xval,double yval,int use_command)1439 ps_calibrate_1 (double xval, double yval, int use_command)
1440 {
1441   HID_Attr_Val vals[3];
1442   FILE *ps_cal_file;
1443   int used_popen = 0, c;
1444 
1445   if (xval > 0 && yval > 0)
1446     {
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))
1450 	    {
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);
1458 	    }
1459       if (guess (yval, 4, &global.calibration_y))
1460 	if (guess (yval, 20, &global.calibration_y))
1461 	  if (guess (yval, 10, &global.calibration_y))
1462 	    {
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);
1470 	    }
1471       return;
1472     }
1473 
1474   if (ps_calib_attribute_list[0].default_val.str_value == NULL)
1475     {
1476       ps_calib_attribute_list[0].default_val.str_value = strdup ("lpr");
1477     }
1478 
1479   if (gui->attribute_dialog (ps_calib_attribute_list, 1, vals, _("Print Calibration Page"), _("Generates a printer calibration page")))
1480     return;
1481 
1482   if (use_command || strchr (vals[0].str_value, '|'))
1483     {
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;
1489     }
1490   else
1491     ps_cal_file = fopen (vals[0].str_value, "w");
1492 
1493   for (c=0; calib_lines[c]; c++)
1494     fputs(calib_lines[c], ps_cal_file);
1495 
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");
1504 
1505   fprintf (ps_cal_file, "%%%%EOF\n");
1506 
1507   if (used_popen)
1508     pclose (ps_cal_file);
1509   else
1510     fclose (ps_cal_file);
1511 }
1512 
1513 static void
ps_calibrate(double xval,double yval)1514 ps_calibrate (double xval, double yval)
1515 {
1516   ps_calibrate_1 (xval, yval, 0);
1517 }
1518 
1519 static void
ps_set_crosshair(int x,int y,int action)1520 ps_set_crosshair (int x, int y, int action)
1521 {
1522 }
1523 
1524 #include "dolists.h"
1525 
1526 HID ps_hid;
1527 static HID_DRAW ps_graphics;
1528 
ps_ps_init(HID * hid)1529 void ps_ps_init (HID *hid)
1530 {
1531   hid->get_export_options = ps_get_export_options;
1532   hid->do_export          = ps_do_export;
1533   hid->parse_arguments    = ps_parse_arguments;
1534   hid->set_layer          = ps_set_layer;
1535   hid->calibrate          = ps_calibrate;
1536   hid->set_crosshair      = ps_set_crosshair;
1537 }
1538 
ps_ps_graphics_init(HID_DRAW * graphics)1539 void ps_ps_graphics_init (HID_DRAW *graphics)
1540 {
1541   graphics->make_gc            = ps_make_gc;
1542   graphics->destroy_gc         = ps_destroy_gc;
1543   graphics->use_mask           = ps_use_mask;
1544   graphics->set_color          = ps_set_color;
1545   graphics->set_line_cap       = ps_set_line_cap;
1546   graphics->set_line_width     = ps_set_line_width;
1547   graphics->set_draw_xor       = ps_set_draw_xor;
1548   graphics->set_draw_faded     = ps_set_draw_faded;
1549   graphics->draw_line          = ps_draw_line;
1550   graphics->draw_arc           = ps_draw_arc;
1551   graphics->draw_rect          = ps_draw_rect;
1552   graphics->fill_circle        = ps_fill_circle;
1553   graphics->fill_polygon       = ps_fill_polygon;
1554   graphics->fill_rect          = ps_fill_rect;
1555 
1556   graphics->draw_pcb_polygon   = ps_draw_pcb_polygon;
1557 }
1558 
1559 void
hid_ps_init()1560 hid_ps_init ()
1561 {
1562   memset (&ps_hid, 0, sizeof (HID));
1563   memset (&ps_graphics, 0, sizeof (HID_DRAW));
1564 
1565   common_nogui_init (&ps_hid);
1566   common_draw_helpers_init (&ps_graphics);
1567   ps_ps_init (&ps_hid);
1568   ps_ps_graphics_init (&ps_graphics);
1569 
1570   ps_hid.struct_size        = sizeof (HID);
1571   ps_hid.name               = "ps";
1572   ps_hid.description        = N_("Postscript export");
1573   ps_hid.exporter           = 1;
1574   ps_hid.poly_before        = 1;
1575 
1576   ps_hid.graphics           = &ps_graphics;
1577 
1578   hid_register_hid (&ps_hid);
1579 
1580   hid_eps_init ();
1581 #include "ps_lists.h"
1582 }
1583