pcb 4.1.1
An interactive printed circuit board layout editor.

ps.c

Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include "config.h"
00003 #endif
00004 
00005 #include <stdio.h>
00006 #include <stdarg.h> /* not used */
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <assert.h> /* not used */
00010 #include <time.h>
00011 
00012 #include "global.h"
00013 #include "data.h"
00014 #include "misc.h"
00015 #include "error.h"
00016 #include "draw.h"
00017 #include "pcb-printf.h"
00018 
00019 #include "hid.h"
00020 #include "hid_draw.h"
00021 #include "../hidint.h"
00022 #include "hid/common/hidnogui.h"
00023 #include "hid/common/draw_helpers.h"
00024 #include "../ps/ps.h"
00025 #include "../../print.h"
00026 #include "hid/common/hidinit.h"
00027 
00028 #ifdef HAVE_LIBDMALLOC
00029 #include <dmalloc.h>
00030 #endif
00031 
00032 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PS function %s.\n", __FUNCTION__); abort()
00033 
00034 static int ps_set_layer (const char *name, int group, int empty);
00035 static void use_gc (hidGC gc);
00036 
00037 typedef struct hid_gc_struct
00038 {
00039   HID *me_pointer;
00040   EndCapStyle cap;
00041   Coord width;
00042   unsigned char r, g, b;
00043   int erase;
00044   int faded;
00045 } hid_gc_struct;
00046 
00047 static const char *medias[] = {
00048   "A0", "A1", "A2", "A3", "A4", "A5",
00049   "A6", "A7", "A8", "A9", "A10",
00050   "B0", "B1", "B2", "B3", "B4", "B5",
00051   "B6", "B7", "B8", "B9", "B10",
00052   "Letter", "11x17", "Ledger",
00053   "Legal", "Executive",
00054   "A-Size", "B-size",
00055   "C-Size", "D-size", "E-size",
00056   "US-Business_Card", "Intl-Business_Card",
00057   0
00058 };
00059 
00060 typedef struct
00061 {
00062   char *name;
00063   Coord Width, Height;
00064   Coord MarginX, MarginY;
00065 } MediaType;
00066 
00067 /*
00068  * Metric ISO sizes in mm.  See http://en.wikipedia.org/wiki/ISO_paper_sizes
00069  *
00070  * A0  841 x 1189
00071  * A1  594 x 841
00072  * A2  420 x 594
00073  * A3  297 x 420
00074  * A4  210 x 297
00075  * A5  148 x 210
00076  * A6  105 x 148
00077  * A7   74 x 105
00078  * A8   52 x  74
00079  * A9   37 x  52
00080  * A10  26 x  37
00081  *
00082  * B0  1000 x 1414
00083  * B1   707 x 1000
00084  * B2   500 x  707
00085  * B3   353 x  500
00086  * B4   250 x  353
00087  * B5   176 x  250
00088  * B6   125 x  176
00089  * B7    88 x  125
00090  * B8    62 x   88
00091  * B9    44 x   62
00092  * B10   31 x   44
00093  *
00094  * awk '{printf("  {\"%s\", %d, %d, MARGINX, MARGINY},\n", $2, $3*100000/25.4, $5*100000/25.4)}'
00095  *
00096  * See http://en.wikipedia.org/wiki/Paper_size#Loose_sizes for some of the other sizes.  The
00097  * {A,B,C,D,E}-Size here are the ANSI sizes and not the architectural sizes.
00098  */
00099 
00100 #define MARGINX MIL_TO_COORD(500)
00101 #define MARGINY MIL_TO_COORD(500)
00102 
00103 static MediaType media_data[] = {
00104   {"A0", MM_TO_COORD(841), MM_TO_COORD(1189), MARGINX, MARGINY},
00105   {"A1", MM_TO_COORD(594), MM_TO_COORD(841), MARGINX, MARGINY},
00106   {"A2", MM_TO_COORD(420), MM_TO_COORD(594), MARGINX, MARGINY},
00107   {"A3", MM_TO_COORD(297), MM_TO_COORD(420), MARGINX, MARGINY},
00108   {"A4", MM_TO_COORD(210), MM_TO_COORD(297), MARGINX, MARGINY},
00109   {"A5", MM_TO_COORD(148), MM_TO_COORD(210), MARGINX, MARGINY},
00110   {"A6", MM_TO_COORD(105), MM_TO_COORD(148), MARGINX, MARGINY},
00111   {"A7", MM_TO_COORD(74), MM_TO_COORD(105), MARGINX, MARGINY},
00112   {"A8", MM_TO_COORD(52), MM_TO_COORD(74), MARGINX, MARGINY},
00113   {"A9", MM_TO_COORD(37), MM_TO_COORD(52), MARGINX, MARGINY},
00114   {"A10", MM_TO_COORD(26), MM_TO_COORD(37), MARGINX, MARGINY},
00115   {"B0", MM_TO_COORD(1000), MM_TO_COORD(1414), MARGINX, MARGINY},
00116   {"B1", MM_TO_COORD(707), MM_TO_COORD(1000), MARGINX, MARGINY},
00117   {"B2", MM_TO_COORD(500), MM_TO_COORD(707), MARGINX, MARGINY},
00118   {"B3", MM_TO_COORD(353), MM_TO_COORD(500), MARGINX, MARGINY},
00119   {"B4", MM_TO_COORD(250), MM_TO_COORD(353), MARGINX, MARGINY},
00120   {"B5", MM_TO_COORD(176), MM_TO_COORD(250), MARGINX, MARGINY},
00121   {"B6", MM_TO_COORD(125), MM_TO_COORD(176), MARGINX, MARGINY},
00122   {"B7", MM_TO_COORD(88), MM_TO_COORD(125), MARGINX, MARGINY},
00123   {"B8", MM_TO_COORD(62), MM_TO_COORD(88), MARGINX, MARGINY},
00124   {"B9", MM_TO_COORD(44), MM_TO_COORD(62), MARGINX, MARGINY},
00125   {"B10", MM_TO_COORD(31), MM_TO_COORD(44), MARGINX, MARGINY},
00126   {"Letter", INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
00127   {"11x17", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
00128   {"Ledger", INCH_TO_COORD(17), INCH_TO_COORD(11), MARGINX, MARGINY},
00129   {"Legal", INCH_TO_COORD(8.5), INCH_TO_COORD(14), MARGINX, MARGINY},
00130   {"Executive", INCH_TO_COORD(7.5), INCH_TO_COORD(10), MARGINX, MARGINY},
00131   {"A-size",  INCH_TO_COORD(8.5), INCH_TO_COORD(11), MARGINX, MARGINY},
00132   {"B-size", INCH_TO_COORD(11), INCH_TO_COORD(17), MARGINX, MARGINY},
00133   {"C-size", INCH_TO_COORD(17), INCH_TO_COORD(22), MARGINX, MARGINY},
00134   {"D-size", INCH_TO_COORD(22), INCH_TO_COORD(34), MARGINX, MARGINY},
00135   {"E-size", INCH_TO_COORD(34), INCH_TO_COORD(44), MARGINX, MARGINY},
00136   {"US-Business_Card", INCH_TO_COORD(3.5), INCH_TO_COORD(2.0), 0, 0},
00137   {"Intl-Business_Card", INCH_TO_COORD(3.375), INCH_TO_COORD(2.125), 0, 0}
00138 };
00139 
00140 #undef MARGINX
00141 #undef MARGINY
00142 
00143 HID_Attribute ps_attribute_list[] = {
00144   /* other HIDs expect this to be first.  */
00145 
00146 /* %start-doc options "91 Postscript Export"
00147 @ftable @code
00148 @item --psfile <string>
00149 Name of the postscript output file. Can contain a path.
00150 @end ftable
00151 %end-doc
00152 */
00153   {N_("psfile"), N_("Postscript output file"),
00154    HID_String, 0, 0, {0, 0, 0}, 0, 0},
00155 #define HA_psfile 0
00156 
00157 /* %start-doc options "91 Postscript Export"
00158 @ftable @code
00159 @cindex drill-helper
00160 @item --drill-helper
00161 Print a centering target in large drill holes.
00162 @end ftable
00163 %end-doc
00164 */
00165   {N_("drill-helper"), N_("Print a centering target in large drill holes"),
00166    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00167 #define HA_drillhelper 1
00168 
00169 /* %start-doc options "91 Postscript Export"
00170 @ftable @code
00171 @cindex align-marks
00172 @item --align-marks
00173 Print alignment marks on each sheet. This is meant to ease alignment during exposure.
00174 @end ftable
00175 %end-doc
00176 */
00177   {N_("align-marks"), N_("Print alignment marks on each sheet"),
00178    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00179 #define HA_alignmarks 2
00180 
00181 /* %start-doc options "91 Postscript Export"
00182 @ftable @code
00183 @item --outline
00184 Print the contents of the outline layer on each sheet.
00185 @end ftable
00186 %end-doc
00187 */
00188   {N_("outline"), N_("Print outline on each sheet"),
00189    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00190 #define HA_outline 3
00191 /* %start-doc options "91 Postscript Export"
00192 @ftable @code
00193 @item --mirror
00194 Print mirror image.
00195 @end ftable
00196 %end-doc
00197 */
00198   {N_("mirror"), N_("Print mirror image of every page"),
00199    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00200 #define HA_mirror 4
00201 
00202 /* %start-doc options "91 Postscript Export"
00203 @ftable @code
00204 @item --fill-page
00205 Scale output to make the board fit the page.
00206 @end ftable
00207 %end-doc
00208 */
00209   {N_("fill-page"), N_("Scale board to fill page"),
00210    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00211 #define HA_fillpage 5
00212 
00213 /* %start-doc options "91 Postscript Export"
00214 @ftable @code
00215 @item --auto-mirror
00216 Print mirror image of appropriate layers.
00217 @end ftable
00218 %end-doc
00219 */
00220   {N_("auto-mirror"), N_("Print mirror image of appropriate layers"),
00221    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00222 #define HA_automirror 6
00223 
00224 /* %start-doc options "91 Postscript Export"
00225 @ftable @code
00226 @item --ps-color
00227 Postscript output in color.
00228 @end ftable
00229 %end-doc
00230 */
00231   {N_("ps-color"), N_("Prints in color"),
00232    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00233 #define HA_color 7
00234 
00235 /* %start-doc options "91 Postscript Export"
00236 @ftable @code
00237 @cindex ps-bloat
00238 @item --ps-bloat <num>
00239 Amount to add to trace/pad/pin edges.
00240 @end ftable
00241 %end-doc
00242 */
00243   {N_("ps-bloat"), N_("Amount to add to trace/pad/pin edges"),
00244    HID_Coord, -MIL_TO_COORD (100), MIL_TO_COORD (100), {0, 0, 0}, 0, 0},
00245 #define HA_psbloat 8
00246 
00247 /* %start-doc options "91 Postscript Export"
00248 @ftable @code
00249 @cindex ps-invert
00250 @item --ps-invert
00251 Draw objects as white-on-black.
00252 @end ftable
00253 %end-doc
00254 */
00255  {N_("ps-invert"), N_("Draw objects as white-on-black"),
00256    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00257 #define HA_psinvert 9
00258 
00259 /* %start-doc options "91 Postscript Export"
00260 @ftable @code
00261 @item --media <media-name>
00262 Size of the media, the postscript is fitted to. The parameter
00263 @code{<media-name>} can be any of the standard names for paper size: @samp{A0}
00264 to @samp{A10}, @samp{B0} to @samp{B10}, @samp{Letter}, @samp{11x17},
00265 @samp{Ledger}, @samp{Legal}, @samp{Executive}, @samp{A-Size}, @samp{B-size},
00266 @samp{C-Size}, @samp{D-size}, @samp{E-size}, @samp{US-Business_Card},
00267 @samp{Intl-Business_Card}.
00268 @end ftable
00269 %end-doc
00270 */
00271   {N_("media"), N_("Media type"),
00272    HID_Enum, 0, 0, {22, 0, 0}, medias, 0},
00273 #define HA_media 10
00274 
00275 /* %start-doc options "91 Postscript Export"
00276 @ftable @code
00277 @cindex psfade
00278 @item --psfade <num>
00279 Fade amount for assembly drawings (0.0=missing, 1.0=solid).
00280 @end ftable
00281 %end-doc
00282 */
00283   {N_("psfade"),
00284    N_("Fade amount for assembly drawings (0.0=missing, 1.0=solid)"),
00285    HID_Real, 0, 1, {0, 0, 0.40}, 0, 0},
00286 #define HA_psfade 11
00287 
00288 /* %start-doc options "91 Postscript Export"
00289 @ftable @code
00290 @item --scale <num>
00291 Scale value to compensate for printer sizing errors (1.0 = full scale).
00292 @end ftable
00293 %end-doc
00294 */
00295   {N_("scale"),
00296    N_("Scale value to compensate for printer sizing errors (1.0 = full scale)"),
00297    HID_Real, 0.01, 4, {0, 0, 1.00}, 0, 0},
00298 #define HA_scale 12
00299 
00300 /* %start-doc options "91 Postscript Export"
00301 @ftable @code
00302 @cindex multi-file
00303 @item --multi-file
00304 Produce multiple files, one per page, instead of a single multi page file.
00305 @end ftable
00306 %end-doc
00307 */
00308   {N_("multi-file"),
00309    N_("Produce multiple files, one per page, instead of a single file"),
00310    HID_Boolean, 0, 0, {0, 0, 0.40}, 0, 0},
00311 #define HA_multifile 13
00312 
00313 /* %start-doc options "91 Postscript Export"
00314 @ftable @code
00315 @item --xcalib <num>
00316 Paper width. Used for x-Axis calibration.
00317 @end ftable
00318 %end-doc
00319 */
00320   {N_("xcalib"), N_("Paper width. Used for x-Axis calibration"),
00321    HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
00322 #define HA_xcalib 14
00323 
00324 /* %start-doc options "91 Postscript Export"
00325 @ftable @code
00326 @item --ycalib <num>
00327 Paper height. Used for y-Axis calibration.
00328 @end ftable
00329 %end-doc
00330 */
00331   {N_("ycalib"), N_("Paper height. Used for y-Axis calibration"),
00332    HID_Real, 0, 0, {0, 0, 1.0}, 0, 0},
00333 #define HA_ycalib 15
00334 
00335 /* %start-doc options "91 Postscript Export"
00336 @ftable @code
00337 @item --drill-copper
00338 Draw drill holes in pins / vias, instead of leaving solid copper.
00339 @end ftable
00340 %end-doc
00341 */
00342   {N_("drill-copper"),
00343    N_("Draw drill holes in pins / vias, instead of leaving solid copper"),
00344    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00345 #define HA_drillcopper 16
00346 
00347 /* %start-doc options "91 Postscript Export"
00348 @ftable @code
00349 @cindex show-legend
00350 @item --show-legend
00351 Print file name and scale on printout.
00352 @end ftable
00353 %end-doc
00354 */
00355   {N_("show-legend"), N_("Print file name and scale on printout"),
00356    HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
00357 #define HA_legend 17
00358 };
00359 
00360 #define NUM_OPTIONS (sizeof(ps_attribute_list)/sizeof(ps_attribute_list[0]))
00361 
00362 REGISTER_ATTRIBUTES (ps_attribute_list)
00363 
00364 /* All file-scope data is in global struct */
00365 static struct {
00366   double calibration_x, calibration_y;
00367 
00368   FILE *f;
00369   int pagecount;
00370   Coord linewidth;
00371   bool print_group[MAX_GROUP];
00372   bool print_layer[MAX_ALL_LAYER];
00373   double fade_ratio;
00374   bool multi_file;
00375   Coord media_width, media_height, ps_width, ps_height;
00376 
00377   const char *filename;
00378   bool drill_helper;
00379   bool align_marks;
00380   bool outline;
00381   bool mirror;
00382   bool fillpage;
00383   bool automirror;
00384   bool incolor;
00385   bool doing_toc;
00386   Coord bloat;
00387   bool invert;
00388   int media_idx;
00389   bool drillcopper;
00390   bool legend;
00391 
00392   LayerType *outline_layer;
00393 
00394   double scale_factor;
00395 
00396   BoxType region;
00397 
00398   HID_Attr_Val ps_values[NUM_OPTIONS];
00399 
00400   bool is_mask;
00401   bool is_drill;
00402   bool is_assy;
00403   bool is_copper;
00404   bool is_paste;
00405 } global;
00406 
00407 static HID_Attribute *
00408 ps_get_export_options (int *n)
00409 {
00410   static char *last_made_filename = 0;
00411   if (PCB)
00412     derive_default_filename(PCB->Filename, &ps_attribute_list[HA_psfile], ".ps", &last_made_filename);
00413 
00414   if (n)
00415     *n = NUM_OPTIONS;
00416   return ps_attribute_list;
00417 }
00418 
00419 static int
00420 layer_stack_sort (const void *va, const void *vb)
00421 {
00422   int a_layer = *(int *) va;
00423   int b_layer = *(int *) vb;
00424   int a_group = GetLayerGroupNumberByNumber (a_layer);
00425   int b_group = GetLayerGroupNumberByNumber (b_layer);
00426 
00427   if (b_group != a_group)
00428     return b_group - a_group;
00429 
00430   return b_layer - a_layer;
00431 }
00432 
00433 void
00434 ps_start_file (FILE *f)
00435 {
00436   time_t currenttime = time( NULL );
00437 
00438   fprintf (f, "%%!PS-Adobe-3.0\n");
00439 
00440   /* Document Structuring Conventions (DCS): */
00441 
00442   /* Start General Header Comments: */
00443 
00444   /*
00445    * %%Title DCS provides text title for the document that is useful
00446    * for printing banner pages.
00447    */
00448   fprintf (f, "%%%%Title: %s\n", PCB->Filename);
00449 
00450   /*
00451    * %%CreationDate DCS indicates the date and time the document was
00452    * created. Neither the date nor time need be in any standard
00453    * format. This comment is meant to be used purely for informational
00454    * purposes, such as printing on banner pages.
00455    */
00456   fprintf (f, "%%%%CreationDate: %s", asctime (localtime (&currenttime)));
00457 
00458   /*
00459    * %%Creator DCS indicates the document creator, usually the name of
00460    * the document composition software.
00461    */
00462   fprintf (f, "%%%%Creator: PCB release: %s " VERSION "\n", Progname);
00463 
00464   /*
00465    * %%Version DCS comment can be used to note the version and
00466    * revision number of a document or resource. A document manager may
00467    * wish to provide version control services, or allow substitution
00468    * of compatible versions/revisions of a resource or document.
00469    *
00470    * The format should be in the form of 'procname':
00471    *  <procname>::= < name> < version> < revision>
00472    *  < name> ::= < text>
00473    *  < version> ::= < real>
00474    *  < revision> ::= < uint>
00475    *
00476    * If a version numbering scheme is not used, these fields should
00477    * still be filled with a dummy value of 0.
00478    *
00479    * There is currently no code in PCB to manage this revision number.
00480    *
00481    */
00482   fprintf (f, "%%%%Version: (PCB %s " VERSION ") 0.0 0\n", Progname );
00483 
00484 
00485   /*
00486    * %%PageOrder DCS is intended to help document managers determine
00487    * the order of pages in the document file, which in turn enables a
00488    * document manager optionally to reorder the pages.  'Ascend'-The
00489    * pages are in ascending order for example, 1-2-3-4-5-6.
00490    */
00491   fprintf (f, "%%%%PageOrder: Ascend\n" );
00492 
00493   /*
00494    * %%Pages: < numpages> | (atend) < numpages> ::= < uint> (Total
00495    * %%number of pages)
00496    *
00497    * %%Pages DCS defines the number of virtual pages that a document
00498    * will image.  (atend) defers the count until the end of the file,
00499    * which is useful for dynamically generated contents.
00500    */
00501   fprintf (f, "%%%%Pages: (atend)\n" );
00502 
00503   /*
00504    * %%DocumentMedia: <name> <width> <height> <weight> <color> <type>
00505    *
00506    * Substitute 0 or "" for N/A.  Width and height are in points
00507    * (1/72").
00508    *
00509    * Media sizes are in PCB units
00510    */
00511   pcb_fprintf (f, "%%%%DocumentMedia: %s %mi %mi 0 \"\" \"\"\n",
00512                media_data[global.media_idx].name,
00513                72 * media_data[global.media_idx].Width,
00514                72 * media_data[global.media_idx].Height);
00515   pcb_fprintf (f, "%%%%DocumentPaperSizes: %s\n", media_data[global.media_idx].name);
00516 
00517   /* End General Header Comments. */
00518 
00519   /* General Body Comments go here. Currently there are none. */
00520 
00521   /*
00522    * %%EndComments DCS indicates an explicit end to the header
00523    * comments of the document.  All global DCS's must preceded
00524    * this.  A blank line gives an implicit end to the comments.
00525    */
00526   fprintf (f, "%%%%EndComments\n\n" );
00527 }
00528 
00529 static void
00530 ps_end_file (FILE *f)
00531 {
00532   /*
00533    * %%Trailer DCS must only occur once at the end of the document
00534    * script.  Any post-processing or cleanup should be contained in
00535    * the trailer of the document, which is anything that follows the
00536    * %%Trailer comment. Any of the document level structure comments
00537    * that were deferred by using the (atend) convention must be
00538    * mentioned in the trailer of the document after the %%Trailer
00539    * comment.
00540    */
00541   fprintf (f, "%%%%Trailer\n" );
00542 
00543   /*
00544    * %%Pages was deferred until the end of the document via the
00545    * (atend) mentioned, in the General Header section.
00546    */
00547   fprintf (f, "%%%%Pages: %d\n", global.pagecount);
00548 
00549   /*
00550    * %%EOF DCS signifies the end of the document. When the document
00551    * manager sees this comment, it issues an end-of-file signal to the
00552    * PostScript interpreter.  This is done so system-dependent file
00553    * endings, such as Control-D and end-of-file packets, do not
00554    * confuse the PostScript interpreter.
00555    */
00556   fprintf (f, "%%%%EOF\n" );
00557 }
00558 
00559 static FILE *
00560 psopen (const char *base, const char *which)
00561 {
00562   FILE *ps_open_file;
00563   char *buf, *suff, *buf2;
00564 
00565   if (!global.multi_file)
00566     return fopen (base, "w");
00567 
00568   buf = (char *)malloc (strlen (base) + strlen (which) + 5);
00569 
00570   suff = (char *)strrchr (base, '.');
00571   if (suff)
00572     {
00573       strcpy (buf, base);
00574       buf2 = strrchr (buf, '.');
00575       sprintf(buf2, ".%s.%s", which, suff+1);
00576     }
00577   else
00578     {
00579       sprintf(buf, "%s.%s.ps", base, which);
00580     }
00581   printf("PS: open %s\n", buf);
00582   ps_open_file = fopen(buf, "w");
00583   free (buf);
00584   return ps_open_file;
00585 }
00586 
00587 /* This is used by other HIDs that use a postscript format, like lpr
00588    or eps.  */
00589 void
00590 ps_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
00591 {
00592   int i;
00593   static int saved_layer_stack[MAX_LAYER];
00594   FlagType save_thindraw;
00595 
00596   save_thindraw = PCB->Flags;
00597   CLEAR_FLAG(THINDRAWFLAG, PCB);
00598   CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
00599   CLEAR_FLAG(CHECKPLANESFLAG, PCB);
00600 
00601   global.f = the_file;
00602   global.drill_helper = options[HA_drillhelper].int_value;
00603   global.align_marks  = options[HA_alignmarks].int_value;
00604   global.outline      = options[HA_outline].int_value;
00605   global.mirror       = options[HA_mirror].int_value;
00606   global.fillpage     = options[HA_fillpage].int_value;
00607   global.automirror   = options[HA_automirror].int_value;
00608   global.incolor      = options[HA_color].int_value;
00609   global.bloat        = options[HA_psbloat].coord_value;
00610   global.invert       = options[HA_psinvert].int_value;
00611   global.fade_ratio   = CLAMP (options[HA_psfade].real_value, 0, 1);
00612   global.media_idx    = options[HA_media].int_value;
00613   global.media_width  = media_data[global.media_idx].Width;
00614   global.media_height = media_data[global.media_idx].Height;
00615   global.ps_width     = global.media_width
00616                         - 2.0 * media_data[global.media_idx].MarginX;
00617   global.ps_height    = global.media_height
00618                         - 2.0 * media_data[global.media_idx].MarginY;
00619   global.scale_factor = options[HA_scale].real_value;
00620   global.calibration_x = options[HA_xcalib].real_value;
00621   global.calibration_y = options[HA_ycalib].real_value;
00622   global.drillcopper  = options[HA_drillcopper].int_value;
00623   global.legend       = options[HA_legend].int_value;
00624 
00625   if (the_file)
00626     ps_start_file (the_file);
00627 
00628   if (global.fillpage)
00629     {
00630       double zx, zy;
00631       if (PCB->MaxWidth > PCB->MaxHeight)
00632         {
00633           zx = global.ps_height / PCB->MaxWidth;
00634           zy = global.ps_width  / PCB->MaxHeight;
00635         }
00636       else
00637         {
00638           zx = global.ps_height / PCB->MaxHeight;
00639           zy = global.ps_width  / PCB->MaxWidth;
00640         }
00641       global.scale_factor *= MIN (zx, zy);
00642     }
00643 
00644   memset (global.print_group, 0, sizeof (global.print_group));
00645   memset (global.print_layer, 0, sizeof (global.print_layer));
00646 
00647   global.outline_layer = NULL;
00648 
00649   for (i = 0; i < max_copper_layer; i++)
00650     {
00651       LayerType *layer = PCB->Data->Layer + i;
00652       if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
00653         global.print_group[GetLayerGroupNumberByNumber (i)] = 1;
00654 
00655       if (strcmp (layer->Name, "outline") == 0 ||
00656           strcmp (layer->Name, "route") == 0)
00657         {
00658           global.outline_layer = layer;
00659         }
00660     }
00661   global.print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
00662   global.print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
00663   for (i = 0; i < max_copper_layer; i++)
00664     if (global.print_group[GetLayerGroupNumberByNumber (i)])
00665       global.print_layer[i] = 1;
00666 
00667   memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
00668   qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
00669 
00670   global.linewidth = -1;
00671   /* reset static vars */
00672   ps_set_layer (NULL, 0, -1);
00673   use_gc (NULL);
00674 
00675   global.region.X1 = 0;
00676   global.region.Y1 = 0;
00677   global.region.X2 = PCB->MaxWidth;
00678   global.region.Y2 = PCB->MaxHeight;
00679 
00680   if (!global.multi_file)
00681     {
00682       /* %%Page DSC requires both a label and an ordinal */
00683       fprintf (the_file, "%%%%Page: TableOfContents 1\n");
00684       fprintf (the_file, "/Times-Roman findfont 14 scalefont setfont\n");
00685       fprintf (the_file, "/rightshow { /s exch def s stringwidth pop -1 mul 0 rmoveto s show } def\n");
00686       fprintf (the_file, "/y 72 9 mul def /toc { 100 y moveto show /y y 24 sub def } bind def\n");
00687       fprintf (the_file, "/tocp { /y y 0 sub def 90 y moveto rightshow } bind def\n");
00688 
00689       global.doing_toc = 1;
00690       global.pagecount = 1;  /* 'pagecount' is modified by hid_expose_callback() call */
00691       hid_expose_callback (&ps_hid, &global.region, 0);
00692     }
00693 
00694   global.pagecount = 1; /* Reset 'pagecount' if single file */
00695   global.doing_toc = 0;
00696   ps_set_layer (NULL, 0, -1);  /* reset static vars */
00697   hid_expose_callback (&ps_hid, &global.region, 0);
00698 
00699   if (the_file)
00700     fprintf (the_file, "showpage\n");
00701 
00702   memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
00703   PCB->Flags = save_thindraw;
00704 }
00705 
00706 static void
00707 ps_do_export (HID_Attr_Val * options)
00708 {
00709   FILE *fh;
00710   int save_ons[MAX_ALL_LAYER];
00711   int i;
00712 
00713   if (!options)
00714     {
00715       ps_get_export_options (0);
00716       for (i = 0; i < NUM_OPTIONS; i++)
00717         global.ps_values[i] = ps_attribute_list[i].default_val;
00718       options = global.ps_values;
00719     }
00720 
00721   global.filename = options[HA_psfile].str_value;
00722   if (!global.filename)
00723     global.filename = "pcb-out.ps";
00724 
00725   global.multi_file = options[HA_multifile].int_value;
00726 
00727   if (global.multi_file)
00728     fh = 0;
00729   else
00730     {
00731       fh = psopen (global.filename, "toc");
00732       if (!fh)
00733         {
00734           perror (global.filename);
00735           return;
00736         }
00737     }
00738 
00739   hid_save_and_show_layer_ons (save_ons);
00740   ps_hid_export_to_file (fh, options);
00741   hid_restore_layer_ons (save_ons);
00742 
00743   global.multi_file = 0;
00744   if (fh)
00745     {
00746       ps_end_file (fh);
00747       fclose (fh);
00748     }
00749 }
00750 
00751 static void
00752 ps_parse_arguments (int *argc, char ***argv)
00753 {
00754   hid_register_attributes (ps_attribute_list, NUM_OPTIONS);
00755   hid_parse_command_line (argc, argv);
00756 }
00757 
00758 static void
00759 corner (FILE *fh, Coord x, Coord y, int dx, int dy)
00760 {
00761   Coord len   = MIL_TO_COORD (2000);
00762   Coord len2  = MIL_TO_COORD (200);
00763   Coord thick = 0;
00764   /*
00765    * Originally 'thick' used thicker lines.  Currently is uses
00766    * Postscript's "device thin" line - i.e. zero width means one
00767    * device pixel.  The code remains in case you want to make them
00768    * thicker - it needs to offset everything so that the *edge* of the
00769    * thick line lines up with the edge of the board, not the *center*
00770    * of the thick line.
00771    */
00772 
00773   pcb_fprintf (fh, "gsave %mi setlinewidth %mi %mi translate %d %d scale\n",
00774                thick * 2, x, y, dx, dy);
00775   pcb_fprintf (fh, "%mi %mi moveto %mi %mi %mi 0 90 arc %mi %mi lineto\n",
00776                len, thick, thick, thick, len2 + thick, thick, len);
00777   if (dx < 0 && dy < 0)
00778     pcb_fprintf (fh, "%mi %mi moveto 0 %mi rlineto\n", len2 * 2 + thick, thick, -len2);
00779   fprintf (fh, "stroke grestore\n");
00780 }
00781 
00782 static int
00783 ps_set_layer (const char *name, int group, int empty)
00784 {
00785   static int lastgroup = -1;
00786   time_t currenttime;
00787   int idx = (group >= 0 && group < max_group)
00788             ? PCB->LayerGroups.Entries[group][0]
00789             : group;
00790   if (name == 0)
00791     name = PCB->Data->Layer[idx].Name;
00792 
00793   if (empty == -1)
00794     lastgroup = -1;
00795   if (empty)
00796     return 0;
00797 
00798   if (idx >= 0 && idx < max_copper_layer && !global.print_layer[idx])
00799     return 0;
00800 
00801   if (strcmp (name, "invisible") == 0)
00802     return 0;
00803 
00804   global.is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
00805   global.is_mask  = (SL_TYPE (idx) == SL_MASK);
00806   global.is_assy  = (SL_TYPE (idx) == SL_ASSY);
00807   global.is_copper = (SL_TYPE (idx) == 0);
00808   global.is_paste  = (SL_TYPE (idx) == SL_PASTE);
00809 #if 0
00810   printf ("Layer %s group %d drill %d mask %d\n", name, group, global.is_drill,
00811           global.is_mask);
00812 #endif
00813 
00814   if (global.doing_toc)
00815     {
00816       if (group < 0 || group != lastgroup)
00817         {
00818           if (global.pagecount == 1)
00819             {
00820               currenttime = time (NULL);
00821               fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
00822 
00823               fprintf (global.f, "(%d.) tocp\n", global.pagecount);
00824               fprintf (global.f, "(Table of Contents \\(This Page\\)) toc\n" );
00825 
00826               fprintf (global.f, "(Created on %s) toc\n", asctime (localtime (&currenttime)));
00827               fprintf (global.f, "( ) tocp\n" );
00828             }
00829 
00830           global.pagecount++;
00831           lastgroup = group;
00832           fprintf (global.f, "(%d.) tocp\n", global.pagecount);
00833         }
00834       fprintf (global.f, "(%s) toc\n", name);
00835       return 0;
00836     }
00837 
00838   if (group < 0 || group != lastgroup)
00839     {
00840       double boffset;
00841       int mirror_this = 0;
00842       lastgroup = group;
00843 
00844       if (global.pagecount != 0)
00845         {
00846           pcb_fprintf (global.f, "showpage\n");
00847         }
00848       global.pagecount++;
00849       if (global.multi_file)
00850         {
00851           if (global.f)
00852             {
00853               ps_end_file (global.f);
00854               fclose (global.f);
00855             }
00856           global.f = psopen (global.filename, layer_type_to_file_name_ex (idx, FNS_fixed, name));
00857           if (!global.f)
00858           {
00859             perror (global.filename);
00860             return 0;
00861           }
00862 
00863           ps_start_file (global.f);
00864         }
00865 
00866       /*
00867        * %%Page DSC comment marks the beginning of the PostScript
00868        * language instructions that describe a particular
00869        * page. %%Page: requires two arguments: a page label and a
00870        * sequential page number. The label may be anything, but the
00871        * ordinal page number must reflect the position of that page in
00872        * the body of the PostScript file and must start with 1, not 0.
00873        */
00874       fprintf (global.f, "%%%%Page: %s %d\n", layer_type_to_file_name_ex (idx, FNS_fixed, name), global.pagecount);
00875 
00876       if (global.mirror)
00877         mirror_this = !mirror_this;
00878       if (global.automirror
00879           &&
00880           ((idx >= 0 && group == GetLayerGroupNumberBySide (BOTTOM_SIDE))
00881            || (idx < 0 && SL_SIDE (idx) == SL_BOTTOM_SIDE)))
00882         mirror_this = !mirror_this;
00883 
00884       fprintf (global.f, "/Helvetica findfont 10 scalefont setfont\n");
00885       if (global.legend)
00886         {
00887           fprintf (global.f, "30 30 moveto (%s) show\n", PCB->Filename);
00888           if (PCB->Name)
00889             fprintf (global.f, "30 41 moveto (%s, %s) show\n",
00890                      PCB->Name, layer_type_to_file_name_ex (idx, FNS_fixed, name));
00891           else
00892             fprintf (global.f, "30 41 moveto (%s) show\n",
00893                      layer_type_to_file_name_ex (idx, FNS_fixed, name));
00894           if (mirror_this)
00895             fprintf (global.f, "( \\(mirrored\\)) show\n");
00896 
00897           if (global.fillpage)
00898             fprintf (global.f, "(, not to scale) show\n");
00899           else
00900             fprintf (global.f, "(, scale = 1:%.3f) show\n", global.scale_factor);
00901         }
00902       fprintf (global.f, "newpath\n");
00903 
00904       pcb_fprintf (global.f, "72 72 scale %mi %mi translate\n",
00905                    global.media_width / 2, global.media_height / 2);
00906 
00907       boffset = global.media_height / 2;
00908       if (PCB->MaxWidth > PCB->MaxHeight)
00909         {
00910           fprintf (global.f, "90 rotate\n");
00911           boffset = global.media_width / 2;
00912           fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_y, global.calibration_x);
00913         }
00914       else
00915         fprintf (global.f, "%g %g scale %% calibration\n", global.calibration_x, global.calibration_y);
00916 
00917       if (mirror_this)
00918         fprintf (global.f, "1 -1 scale\n");
00919 
00920       fprintf (global.f, "%g dup neg scale\n",
00921                (SL_TYPE (idx) == SL_FAB) ? 1.0 : global.scale_factor);
00922       pcb_fprintf (global.f, "%mi %mi translate\n", -PCB->MaxWidth / 2, -PCB->MaxHeight / 2);
00923 
00924       /* Keep the drill list from falling off the left edge of the paper,
00925        * even if it means some of the board falls off the right edge.
00926        * If users don't want to make smaller boards, or use fewer drill
00927        * sizes, they can always ignore this sheet. */
00928       if (SL_TYPE (idx) == SL_FAB) {
00929         Coord natural = boffset - MIL_TO_COORD(500) - PCB->MaxHeight / 2;
00930         Coord needed  = PrintFab_overhang ();
00931         pcb_fprintf (global.f, "%% PrintFab overhang natural %mi, needed %mi\n", natural, needed);
00932         if (needed > natural)
00933           pcb_fprintf (global.f, "0 %mi translate\n", needed - natural);
00934       }
00935 
00936       if (global.invert)
00937         {
00938           fprintf (global.f, "/gray { 1 exch sub setgray } bind def\n");
00939           fprintf (global.f,
00940                    "/rgb { 1 1 3 { pop 1 exch sub 3 1 roll } for setrgbcolor } bind def\n");
00941         }
00942       else
00943         {
00944           fprintf (global.f, "/gray { setgray } bind def\n");
00945           fprintf (global.f, "/rgb { setrgbcolor } bind def\n");
00946         }
00947 
00948       if ((global.outline && !global.outline_layer) || global.invert)
00949         {
00950           pcb_fprintf (global.f,
00951                        "0 setgray 0 setlinewidth 0 0 moveto 0 "
00952                        "%mi lineto %mi %mi lineto %mi 0 lineto closepath %s\n",
00953                        PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight, PCB->MaxWidth,
00954                        global.invert ? "fill" : "stroke");
00955         }
00956 
00957       if (global.align_marks)
00958         {
00959           corner (global.f, 0, 0, -1, -1);
00960           corner (global.f, PCB->MaxWidth, 0, 1, -1);
00961           corner (global.f, PCB->MaxWidth, PCB->MaxHeight, 1, 1);
00962           corner (global.f, 0, PCB->MaxHeight, -1, 1);
00963         }
00964 
00965       global.linewidth = -1;
00966       use_gc (NULL);  /* reset static vars */
00967 
00968       fprintf (global.f,
00969               "/ts 1 def\n"
00970               "/ty ts neg def /tx 0 def /Helvetica findfont ts scalefont setfont\n"
00971               "/t { moveto lineto stroke } bind def\n"
00972               "/dr { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
00973               "      x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath stroke } bind def\n"
00974               "/r { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"
00975               "     x1 y1 moveto x1 y2 lineto x2 y2 lineto x2 y1 lineto closepath fill } bind def\n"
00976               "/c { 0 360 arc fill } bind def\n"
00977               "/a { gsave setlinewidth translate scale 0 0 1 5 3 roll arc stroke grestore} bind def\n");
00978       if (global.drill_helper)
00979         pcb_fprintf (global.f,
00980                     "/dh { gsave %mi setlinewidth 0 gray %mi 0 360 arc stroke grestore} bind def\n",
00981                     (Coord) MIN_PINORVIAHOLE, (Coord) (MIN_PINORVIAHOLE * 3 / 2));
00982     }
00983 #if 0
00984   /* Try to outsmart ps2pdf's heuristics for page rotation, by putting
00985    * text on all pages -- even if that text is blank */
00986   if (SL_TYPE (idx) != SL_FAB)
00987     fprintf (global.f,
00988              "gsave tx ty translate 1 -1 scale 0 0 moveto (Layer %s) show grestore newpath /ty ty ts sub def\n",
00989              name);
00990   else
00991     fprintf (global.f, "gsave tx ty translate 1 -1 scale 0 0 moveto ( ) show grestore newpath /ty ty ts sub def\n");
00992 #endif
00993 
00994   /* If we're printing a copper layer other than the outline layer,
00995      and we want to "print outlines", and we have an outline layer,
00996      print the outline layer on this layer also.  */
00997   if (global.outline &&
00998       (global.is_copper || (strcmp(name, "topassembly") == 0) || (strcmp(name, "bottomassembly") == 0)) &&
00999       global.outline_layer != NULL &&
01000       global.outline_layer != PCB->Data->Layer+idx &&
01001       strcmp (name, "outline") != 0 &&
01002       strcmp (name, "route") != 0
01003       )
01004     {
01005       DrawLayer (global.outline_layer, &global.region);
01006     }
01007 
01008   return 1;
01009 }
01010 
01011 static hidGC
01012 ps_make_gc (void)
01013 {
01014   hidGC rv = (hidGC) calloc (1, sizeof (hid_gc_struct));
01015   rv->me_pointer = &ps_hid;
01016   rv->cap = Trace_Cap;
01017   return rv;
01018 }
01019 
01020 static void
01021 ps_destroy_gc (hidGC gc)
01022 {
01023   free (gc);
01024 }
01025 
01026 static void
01027 ps_use_mask (enum mask_mode mode)
01028 {
01029   /* does nothing */
01030 }
01031 
01032 static void
01033 ps_set_color (hidGC gc, const char *name)
01034 {
01035   if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
01036     {
01037       gc->r = gc->g = gc->b = 255;
01038       gc->erase = 1;
01039     }
01040   else if (global.incolor)
01041     {
01042       int r, g, b;
01043       sscanf (name + 1, "%02x%02x%02x", &r, &g, &b);
01044       gc->r = r;
01045       gc->g = g;
01046       gc->b = b;
01047       gc->erase = 0;
01048     }
01049   else
01050     {
01051       gc->r = gc->g = gc->b = 0;
01052       gc->erase = 0;
01053     }
01054 }
01055 
01056 static void
01057 ps_set_line_cap (hidGC gc, EndCapStyle style)
01058 {
01059   gc->cap = style;
01060 }
01061 
01062 static void
01063 ps_set_line_width (hidGC gc, Coord width)
01064 {
01065   gc->width = width;
01066 }
01067 
01068 static void
01069 ps_set_draw_xor (hidGC gc, int xor_)
01070 {
01071   ;
01072 }
01073 
01074 static void
01075 ps_set_draw_faded (hidGC gc, int faded)
01076 {
01077   gc->faded = faded;
01078 }
01079 
01080 static void
01081 use_gc (hidGC gc)
01082 {
01083   static int lastcap = -1;
01084   static int lastcolor = -1;
01085 
01086   if (gc == NULL)
01087     {
01088       lastcap = lastcolor = -1;
01089       return;
01090     }
01091   if (gc->me_pointer != &ps_hid)
01092     {
01093       fprintf (stderr, "Fatal: GC from another HID passed to ps HID\n");
01094       abort ();
01095     }
01096   if (global.linewidth != gc->width)
01097     {
01098       pcb_fprintf (global.f, "%mi setlinewidth\n",
01099                    gc->width + (gc->erase ? -2 : 2) * global.bloat);
01100       global.linewidth = gc->width;
01101     }
01102   if (lastcap != gc->cap)
01103     {
01104       int c;
01105       switch (gc->cap)
01106         {
01107         case Round_Cap:
01108         case Trace_Cap:
01109           c = 1;
01110           break;
01111         default:
01112         case Square_Cap:
01113           c = 2;
01114           break;
01115         }
01116       fprintf (global.f, "%d setlinecap %d setlinejoin\n", c, c);
01117       lastcap = gc->cap;
01118     }
01119 #define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
01120   if (lastcolor != CBLEND (gc))
01121     {
01122       if (global.is_drill || global.is_mask)
01123         {
01124           fprintf (global.f, "%d gray\n", gc->erase ? 0 : 1);
01125           lastcolor = 0;
01126         }
01127       else
01128         {
01129           double r, g, b;
01130           r = gc->r;
01131           g = gc->g;
01132           b = gc->b;
01133           if (gc->faded)
01134             {
01135               r = (1 - global.fade_ratio) * 255 + global.fade_ratio * r;
01136               g = (1 - global.fade_ratio) * 255 + global.fade_ratio * g;
01137               b = (1 - global.fade_ratio) * 255 + global.fade_ratio * b;
01138             }
01139           if (gc->r == gc->g && gc->g == gc->b)
01140             fprintf (global.f, "%g gray\n", r / 255.0);
01141           else
01142             fprintf (global.f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
01143           lastcolor = CBLEND (gc);
01144         }
01145     }
01146 }
01147 
01148 static void
01149 ps_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01150 {
01151   use_gc (gc);
01152   pcb_fprintf (global.f, "%mi %mi %mi %mi dr\n", x1, y1, x2, y2);
01153 }
01154 
01155 static void ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
01156 static void ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
01157 
01158 static void
01159 ps_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01160 {
01161 #if 0
01162   /* If you're etching your own paste mask, this will reduce the
01163      amount of brass you need to etch by drawing outlines for large
01164      pads.  See also ps_fill_rect.  */
01165   if (is_paste && gc->width > 2500 && gc->cap == Square_Cap
01166       && (x1 == x2 || y1 == y2))
01167     {
01168       Coord t, w;
01169       if (x1 > x2)
01170         { t = x1; x1 = x2; x2 = t; }
01171       if (y1 > y2)
01172         { t = y1; y1 = y2; y2 = t; }
01173       w = gc->width/2;
01174       ps_fill_rect (gc, x1-w, y1-w, x2+w, y2+w);
01175       return;
01176     }
01177 #endif
01178   if (x1 == x2 && y1 == y2)
01179     {
01180       Coord w = gc->width / 2;
01181       if (gc->cap == Square_Cap)
01182         ps_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
01183       else
01184         ps_fill_circle (gc, x1, y1, w);
01185       return;
01186     }
01187   use_gc (gc);
01188   pcb_fprintf (global.f, "%mi %mi %mi %mi t\n", x1, y1, x2, y2);
01189 }
01190 
01191 static void
01192 ps_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
01193              Angle start_angle, Angle delta_angle)
01194 {
01195   Angle sa, ea;
01196   double linewidth;
01197 
01198   if (delta_angle > 0)
01199     {
01200       sa = start_angle;
01201       ea = start_angle + delta_angle;
01202     }
01203   else
01204     {
01205       sa = start_angle + delta_angle;
01206       ea = start_angle;
01207     }
01208 
01209   use_gc (gc);
01210 
01211   /* Other than pcb's screen renderer, PostScript (at least GhostScript)
01212      internally limits linewidth to (diameter / 2), so no drawing of a dot with
01213      a circle of zero diameter. Compensate for this by making diameter larger
01214      and line width thinner.
01215 
01216      Handling of this case currently has only circles in mind, no ellipses.
01217      The regular case works with ellipses, too. */
01218   if (width < global.linewidth)
01219     {
01220       Coord outer_radius;
01221 
01222       outer_radius = width + global.linewidth / 2 + global.bloat;
01223       width = height = outer_radius / 2;
01224       linewidth = 2.0;
01225     }
01226   else
01227     linewidth = (double) (global.linewidth + 2 * global.bloat) / (double) width;
01228 
01229   /* The line of PostScript written here is a bit odd; linewidth isn't
01230      absolute, but relative to circle diameter. This is neccessary for
01231      ellipses, which are drawn by streching (transforming) a circle. */
01232   pcb_fprintf (global.f, "%ma %ma %mi %mi %mi %mi %`g a\n",
01233                sa, ea, -width, height, cx, cy, linewidth);
01234 
01235 }
01236 
01237 static void
01238 ps_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
01239 {
01240   use_gc (gc);
01241   if (!gc->erase || !global.is_copper || global.drillcopper)
01242     {
01243       if (gc->erase && global.is_copper && global.drill_helper
01244           && radius >= PCB->minDrill / 4)
01245         radius = PCB->minDrill / 4;
01246       pcb_fprintf (global.f, "%mi %mi %mi c\n",
01247                    cx, cy, radius + (gc->erase ? -1 : 1) * global.bloat);
01248     }
01249 }
01250 
01251 static void
01252 ps_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
01253 {
01254   int i;
01255   char *op = "moveto";
01256   use_gc (gc);
01257   for (i = 0; i < n_coords; i++)
01258     {
01259       pcb_fprintf (global.f, "%mi %mi %s\n", x[i], y[i], op);
01260       op = "lineto";
01261     }
01262   fprintf (global.f, "fill\n");
01263 }
01264 
01265 static void
01266 fill_polyarea (hidGC gc, POLYAREA * pa, const BoxType * clip_box)
01267 {
01268   /* Ignore clip_box, just draw everything */
01269 
01270   VNODE *v;
01271   PLINE *pl;
01272   char *op;
01273 
01274   use_gc (gc);
01275 
01276   pl = pa->contours;
01277 
01278   do
01279     {
01280       v = pl->head.next;
01281       op = "moveto";
01282       do
01283         {
01284           pcb_fprintf (global.f, "%mi %mi %s\n", v->point[0], v->point[1], op);
01285           op = "lineto";
01286         }
01287       while ((v = v->next) != pl->head.next);
01288     }
01289   while ((pl = pl->next) != NULL);
01290 
01291   fprintf (global.f, "fill\n");
01292 }
01293 
01294 static void
01295 ps_draw_pcb_polygon (hidGC gc, PolygonType * poly, const BoxType * clip_box)
01296 {
01297   if (!poly->Clipped)
01298     return;
01299   fill_polyarea (gc, poly->Clipped, clip_box);
01300   if (TEST_FLAG (FULLPOLYFLAG, poly))
01301     {
01302       POLYAREA *pa;
01303 
01304       for (pa = poly->Clipped->f; pa != poly->Clipped; pa = pa->f)
01305         fill_polyarea (gc, pa, clip_box);
01306     }
01307 }
01308 
01309 static void
01310 ps_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01311 {
01312   use_gc (gc);
01313   if (x1 > x2)
01314     {
01315       Coord t = x1;
01316       x1 = x2;
01317       x2 = t;
01318     }
01319   if (y1 > y2)
01320     {
01321       Coord t = y1;
01322       y1 = y2;
01323       y2 = t;
01324     }
01325 #if 0
01326   /* See comment in ps_draw_line.  */
01327   if (is_paste && (x2-x1)>2500 && (y2-y1)>2500)
01328     {
01329       linewidth = 1000;
01330       lastcap = Round_Cap;
01331       fprintf(f, "1000 setlinewidth 1 setlinecap 1 setlinejoin\n");
01332       fprintf(f, "%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
01333               x1+500-bloat, y1+500-bloat,
01334               x1+500-bloat, y2-500+bloat,
01335               x2-500+bloat, y2-500+bloat,
01336               x2-500+bloat, y1+500-bloat);
01337       return;
01338     }
01339 #endif
01340   pcb_fprintf (global.f, "%mi %mi %mi %mi r\n",
01341                x1 - global.bloat, y1 - global.bloat,
01342                x2 + global.bloat, y2 + global.bloat);
01343 }
01344 
01345 HID_Attribute ps_calib_attribute_list[] = {
01346   {N_("lprcommand"), N_("Command to print"),
01347    HID_String, 0, 0, {0, 0, 0}, 0, 0},
01348 };
01349 
01350 static const char * const calib_lines[] = {
01351   "%!PS-Adobe-3.0\n",
01352   "%%Title: Calibration Page\n",
01353   "%%PageOrder: Ascend\n",
01354   "%%Pages: 1\n",
01355   "%%EndComments\n",
01356   "\n",
01357   "%%Page: Calibrate 1\n",
01358   "72 72 scale\n",
01359   "\n",
01360   "0 setlinewidth\n",
01361   "0.375 0.375 moveto\n",
01362   "8.125 0.375 lineto\n",
01363   "8.125 10.625 lineto\n",
01364   "0.375 10.625 lineto\n",
01365   "closepath stroke\n",
01366   "\n",
01367   "0.5 0.5 translate\n",
01368   "0.001 setlinewidth\n",
01369   "\n",
01370   "/Times-Roman findfont 0.2 scalefont setfont\n",
01371   "\n",
01372   "/sign {\n",
01373   "    0 lt { -1 } { 1 } ifelse\n",
01374   "} def\n",
01375   "\n",
01376   "/cbar {\n",
01377   "    /units exch def\n",
01378   "    /x exch def\n",
01379   "    /y exch def  \n",
01380   "\n",
01381   "    /x x sign 0.5 mul def\n",
01382   "\n",
01383   "    0 setlinewidth\n",
01384   "    newpath x y 0.25 0 180 arc gsave 0.85 setgray fill grestore closepath stroke\n",
01385   "    newpath x 0 0.25 180 360 arc gsave 0.85 setgray fill grestore closepath stroke\n",
01386   "    0.001 setlinewidth\n",
01387   "\n",
01388   "    x 0 moveto\n",
01389   "    x y lineto\n",
01390   "%    -0.07 -0.2 rlineto 0.14 0 rmoveto -0.07 0.2 rlineto\n",
01391   "    x y lineto\n",
01392   "    -0.1 0 rlineto 0.2 0 rlineto\n",
01393   "    stroke\n",
01394   "    x 0 moveto\n",
01395   "%    -0.07 0.2 rlineto 0.14 0 rmoveto -0.07 -0.2 rlineto\n",
01396   "    x 0 moveto\n",
01397   "    -0.1 0 rlineto 0.2 0 rlineto\n",
01398   "     stroke\n",
01399   "\n",
01400   "    x 0.1 add\n",
01401   "    y 0.2 sub moveto\n",
01402   "    units show\n",
01403   "} bind def\n",
01404   "\n",
01405   "/y 9 def\n",
01406   "/t {\n",
01407   "    /str exch def\n",
01408   "    1.5 y moveto str show\n",
01409   "    /y y 0.25 sub def\n",
01410   "} bind def\n",
01411   "\n",
01412   "(Please measure between the flat faces of ONE pair of semi-circles on)t\n",
01413   "(both X and Y in the indicated units. Enter these values as X and Y)t\n",
01414   "(respectively. One member of each pair must be one of the semicircles)t\n",
01415   "(in the lower left corner. Nominal lengths on X are 4 in, 15 cm and 7.5 in.)t\n",
01416   "(Nominal lengths on Y are 4 in, 20 cm and 10 in.)t\n",
01417   "()t\n",
01418   "(The large box is 10.25 by 7.75 inches and is not used for calibration.)t\n",  "\n",
01419   "/in { } bind def\n",
01420   "/cm { 2.54 div } bind def\n",
01421   "/mm { 25.4 div } bind def\n",
01422   "\n",
01423   0
01424 };
01425 
01426 static int
01427 guess(double val, double close_to, double *calib)
01428 {
01429   if (val >= close_to * 0.9
01430       && val <= close_to * 1.1)
01431     {
01432       *calib = close_to / val;
01433       return 0;
01434     }
01435   return 1;
01436 }
01437 
01438 void
01439 ps_calibrate_1 (double xval, double yval, int use_command)
01440 {
01441   HID_Attr_Val vals[3];
01442   FILE *ps_cal_file;
01443   int used_popen = 0, c;
01444 
01445   if (xval > 0 && yval > 0)
01446     {
01447       if (guess (xval, 4, &global.calibration_x))
01448         if (guess (xval, 15, &global.calibration_x))
01449           if (guess (xval, 7.5, &global.calibration_x))
01450             {
01451               if (xval < 2)
01452                 ps_attribute_list[HA_xcalib].default_val.real_value =
01453                   global.calibration_x = xval;
01454               else
01455                 Message(_("X value of %g is too far off.\n"
01456                         "Expecting it near: %.1f, %.1f, %.1f, %.1f\n"),
01457                         xval, 1.0, 4.0, 15.0, 7.5);
01458             }
01459       if (guess (yval, 4, &global.calibration_y))
01460         if (guess (yval, 20, &global.calibration_y))
01461           if (guess (yval, 10, &global.calibration_y))
01462             {
01463               if (yval < 2)
01464                 ps_attribute_list[HA_ycalib].default_val.real_value =
01465                   global.calibration_y = yval;
01466               else
01467                 Message(_("Y value of %g is too far off.\n"
01468                         "Expecting it near: %.1f, %.1f, %.1f, %.1f\n"),
01469                         yval, 1.0, 4.0, 20.0, 10.0);
01470             }
01471       return;
01472     }
01473 
01474   if (ps_calib_attribute_list[0].default_val.str_value == NULL)
01475     {
01476       ps_calib_attribute_list[0].default_val.str_value = strdup ("lpr");
01477     }
01478 
01479   if (gui->attribute_dialog (ps_calib_attribute_list, 1, vals, _("Print Calibration Page"), _("Generates a printer calibration page")))
01480     return;
01481 
01482   if (use_command || strchr (vals[0].str_value, '|'))
01483     {
01484       const char *cmd = vals[0].str_value;
01485       while (*cmd == ' ' || *cmd == '|')
01486         cmd ++;
01487       ps_cal_file = popen (cmd, "w");
01488       used_popen = 1;
01489     }
01490   else
01491     ps_cal_file = fopen (vals[0].str_value, "w");
01492 
01493   for (c=0; calib_lines[c]; c++)
01494     fputs(calib_lines[c], ps_cal_file);
01495 
01496   fprintf (ps_cal_file, "4 in 0.5 (Y in nom=4) cbar\n");
01497   fprintf (ps_cal_file, "20 cm 1.5 (Y cm nom=20) cbar\n");
01498   fprintf (ps_cal_file, "10 in 2.5 (Y in nom=10) cbar\n");
01499   fprintf (ps_cal_file, "-90 rotate\n");
01500   fprintf (ps_cal_file, "4 in -0.5 (X in nom=4) cbar\n");
01501   fprintf (ps_cal_file, "15 cm -1.5 (X cm nom=15) cbar\n");
01502   fprintf (ps_cal_file, "7.5 in -2.5 (X in nom=7.5) cbar\n");
01503   fprintf (ps_cal_file, "showpage\n");
01504 
01505   fprintf (ps_cal_file, "%%%%EOF\n");
01506 
01507   if (used_popen)
01508     pclose (ps_cal_file);
01509   else
01510     fclose (ps_cal_file);
01511 }
01512 
01513 static void
01514 ps_calibrate (double xval, double yval)
01515 {
01516   ps_calibrate_1 (xval, yval, 0);
01517 }
01518 
01519 static void
01520 ps_set_crosshair (int x, int y, int action)
01521 {
01522 }
01523 
01524 #include "dolists.h"
01525 
01526 HID ps_hid;
01527 static HID_DRAW ps_graphics;
01528 
01529 void ps_ps_init (HID *hid)
01530 {
01531   hid->get_export_options = ps_get_export_options;
01532   hid->do_export          = ps_do_export;
01533   hid->parse_arguments    = ps_parse_arguments;
01534   hid->set_layer          = ps_set_layer;
01535   hid->calibrate          = ps_calibrate;
01536   hid->set_crosshair      = ps_set_crosshair;
01537 }
01538 
01539 void ps_ps_graphics_init (HID_DRAW *graphics)
01540 {
01541   graphics->make_gc            = ps_make_gc;
01542   graphics->destroy_gc         = ps_destroy_gc;
01543   graphics->use_mask           = ps_use_mask;
01544   graphics->set_color          = ps_set_color;
01545   graphics->set_line_cap       = ps_set_line_cap;
01546   graphics->set_line_width     = ps_set_line_width;
01547   graphics->set_draw_xor       = ps_set_draw_xor;
01548   graphics->set_draw_faded     = ps_set_draw_faded;
01549   graphics->draw_line          = ps_draw_line;
01550   graphics->draw_arc           = ps_draw_arc;
01551   graphics->draw_rect          = ps_draw_rect;
01552   graphics->fill_circle        = ps_fill_circle;
01553   graphics->fill_polygon       = ps_fill_polygon;
01554   graphics->fill_rect          = ps_fill_rect;
01555 
01556   graphics->draw_pcb_polygon   = ps_draw_pcb_polygon;
01557 }
01558 
01559 void
01560 hid_ps_init ()
01561 {
01562   memset (&ps_hid, 0, sizeof (HID));
01563   memset (&ps_graphics, 0, sizeof (HID_DRAW));
01564 
01565   common_nogui_init (&ps_hid);
01566   common_draw_helpers_init (&ps_graphics);
01567   ps_ps_init (&ps_hid);
01568   ps_ps_graphics_init (&ps_graphics);
01569 
01570   ps_hid.struct_size        = sizeof (HID);
01571   ps_hid.name               = "ps";
01572   ps_hid.description        = N_("Postscript export");
01573   ps_hid.exporter           = 1;
01574   ps_hid.poly_before        = 1;
01575 
01576   ps_hid.graphics           = &ps_graphics;
01577 
01578   hid_register_hid (&ps_hid);
01579 
01580   hid_eps_init ();
01581 #include "ps_lists.h"
01582 }