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 (¤ttime)));
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 (¤ttime)));
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