home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / ps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  44.1 KB  |  1,543 lines  |  [TEXT/R*ch]

  1. /* PostScript(tm) printing for Xconq.
  2.    Copyright (C) 1994, 1995 Massimo Campostrini & Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /*
  10.     Problems
  11.     --------
  12.    Terrain prints reasonably with terrain_gray = 0.5 and
  13. terrain_dither = 0 (at least on a LaserWriter Pro 630 at 600dpi).
  14. terrain_gray = 0 prints terrain icons which are easier to distinguish,
  15. but make many terrains too dark.  Perhaps design new, lighter bitmaps
  16. for terrains?
  17.    Small feature legends are unreadable over dark terrains and connections. 
  18. Legends over terrain improve if terrain_gray is set to 0.75, but then 
  19. terrains are hard to distinguish.  Some kind of masking is in order, at 
  20. least for small legends.
  21.  
  22.     TODO
  23.     ----
  24.    How to mask feature legends?  Use "reverse outline" fonts (from 
  25. "outline.ps" in the cookbook)?  Or just blank out a rectangle under small 
  26. legends, like what is now done for unit names?
  27.    The front end should announce "printing view to file..." and "done"
  28. [done for xconq].
  29.    Implement selection of the region to print.
  30.    Read and interpret `embed' forms.
  31.    Use border/connection bitmaps?  Or differentiate otherwise
  32. between borders or connections?
  33.    Print more fancy legends, e.g., list of side names/emblems 
  34. (on a separate page?)
  35.    Merge parameters better into xconq: initialize by resources and let
  36. each front-end change them (MAC & X should pop-up a window  [X done]).
  37.    If a bitmap is missing but a color image is present, convert it? How?
  38.    Better choice of unit to display (for crowded cells).  How?
  39.    Fix priniting of unit names spanning more than one cell.
  40.    Mark friendly/enemy units.  How?
  41.    More cleanup.
  42.  
  43.     How to customize
  44.     ----------------
  45.    The output look is controlled by a bunch of variables in the
  46. routine init_print.  Their default values are "wired in" into the code,
  47. but xconq may pop-up a configuration window.  Check that the default 
  48. `page_width' and `page_height' fit your paper sheets.
  49.  
  50.    Mail me suggestions and comments.  I would especially appreciate
  51. suggestions for generating cleaner, faster, more compact PostScript.
  52.  
  53.         Massimo Campostrini,
  54. Istituto Nazionale di Fisica Nucleare, Sezione di Pisa,
  55. Piazza Torricelli 2, I56126 Pisa, Italy  ||  Phone: (+39)(50)911272
  56. Internet: campo@sunthpi3.difi.unipi.it   ||  Fax:   (+39)(50)48277  */
  57.  
  58. #include "conq.h"
  59. extern int num_features PARAMS ((void));
  60. #include "print.h"
  61. #include "imf.h"
  62. #include "ui.h"
  63. #include <math.h>
  64.  
  65. PrintParameters *pp = NULL;
  66.  
  67. #define on_page(x,y) (((x)>=0) && ((x)<width) && ((y)>=0) && ((y)<height))
  68.  
  69. /* masks for name placing */
  70. #define N_U  '\001'
  71. #define N_D  '\002'
  72. #define N_BK '\020'
  73.  
  74. #define name_at(x,y)  (name_layer[width*((y)+1)+(x)+1])
  75.  
  76. #define GRAY_SCALE 16          /* precision for dithering */
  77. /* length of a hexadecimal string encoding a w*h rectangle */
  78. #define area_leng(w,h) ((((w)+7)/8) * (h) * 2)
  79.  
  80. /* this will allow images up to 64*64 */
  81. #define PSBUFSIZE 1148
  82.  
  83. static int name_dir[] =
  84.   { EAST, WEST, SOUTHEAST, NORTHEAST, SOUTHWEST, NORTHWEST };
  85.  
  86. static char *buffer;
  87.  
  88. static char *escbuf;
  89.  
  90. static Side *ps_side;
  91.  
  92. static char zero[] = "00";
  93.  
  94. static int img_cooked = 0, terr_scale, terr_gray, x0, width, height;
  95.  
  96. static double ru, rtw, rth, asym, sqrt3;
  97.  
  98. static double cell_grid_width, border_width, connection_width;
  99.  
  100. /* h = hex height; c = vert. distance between hex centers;
  101.    w = hex width = horiz. distance between hex centers.
  102.    For undeformed hexes, c = h*3/4, w = h*sqrt(3)/2;
  103.    the following asymmetry allows 8x8 blocks to tile properly,
  104.    i.e. hex_w%16==0 && hex_c%8==0
  105.    Moreover, the code assumes  hex_w%4==0 && hex_c%2==0 && hex_h%4==0 */
  106. static int hex_h = 32;
  107. static int hex_c = 24;
  108. static int hex_w = 32;
  109. /* unit icon size (compare to hex_w, *not* to hex_h) */
  110. static int unit_w = 16;
  111.  
  112. /* PostScript fonts description */
  113.  
  114. static char *ps_name[3] = {
  115.   "Helvetica-Narrow",
  116.   "Helvetica",
  117.   "Helvetica-Bold"
  118. };
  119.  
  120. /* height of a capital letter in PostScript Helvetica,
  121.    in thousands of the "nominal size" */
  122. static int ps_height = 729;
  123.  
  124. /* Width of letters in PostScript Helvetica fonts,
  125.    in thousands of the "nominal size" */
  126. /* (This is cut down to 128 because wimpy machines like Macs
  127.    don't have much room for initialized data, and I think all
  128.    the strings have only 7-bit chars. ps_string_width will
  129.    warn if not.) */
  130. static short ps_width[3][128 /*256*/] = {
  131. /* font 0 is Helvetica-Narrow */
  132.   {
  133.     0,    0,    0,    0,    0,    0,    0,    0,
  134.     0,    0,    0,    0,    0,    0,    0,    0,
  135.     0,    0,    0,    0,    0,    0,    0,    0,
  136.     0,    0,    0,    0,    0,    0,    0,    0,
  137.   228,  228,  291,  456,  456,  729,  547,  182,
  138.   273,  273,  319,  479,  228,  273,  228,  228,
  139.   456,  456,  456,  456,  456,  456,  456,  456,
  140.   456,  456,  228,  228,  479,  479,  479,  456,
  141.   832,  547,  547,  592,  592,  547,  501,  638,
  142.   592,  228,  410,  547,  456,  683,  592,  638,
  143.   547,  638,  592,  547,  501,  592,  547,  774,
  144.   547,  547,  501,  228,  228,  228,  385,  456,
  145.   182,  456,  456,  410,  456,  456,  228,  456,
  146.   456,  182,  182,  410,  182,  683,  456,  456,
  147.   456,  456,  273,  410,  228,  456,  410,  592,
  148.   410,  410,  410,  274,  213,  274,  479,    0  /*,
  149.     0,    0,    0,    0,    0,    0,    0,    0,
  150.     0,    0,    0,    0,    0,    0,    0,    0,
  151.     0,    0,    0,    0,    0,    0,    0,    0,
  152.     0,    0,    0,    0,    0,    0,    0,    0,
  153.     0,  273,  456,  456,  137,  456,  456,  456,
  154.   456,  157,  273,  456,  273,  273,  410,  410,
  155.     0,  456,  456,  456,  228,    0,  440,  287,
  156.   182,  273,  273,  456,  820,  820,    0,  501,
  157.     0,  273,  273,  273,  273,  273,  273,  273,
  158.   273,    0,  273,  273,    0,  273,  273,  273,
  159.   820,    0,    0,    0,    0,    0,    0,    0,
  160.     0,    0,    0,    0,    0,    0,    0,    0,
  161.     0,  820,    0,  303,    0,    0,    0,    0,
  162.   456,  638,  820,  299,    0,    0,    0,    0,
  163.     0,  729,    0,    0,    0,  228,    0,    0,
  164.   182,  501,  774,  501,    0,    0,    0,    0  */ },
  165. /* font 1 is Helvetica */
  166.   {
  167.     0,    0,    0,    0,    0,    0,    0,    0,
  168.     0,    0,    0,    0,    0,    0,    0,    0,
  169.     0,    0,    0,    0,    0,    0,    0,    0,
  170.     0,    0,    0,    0,    0,    0,    0,    0,
  171.   278,  278,  355,  556,  556,  889,  667,  222,
  172.   333,  333,  389,  584,  278,  333,  278,  278,
  173.   556,  556,  556,  556,  556,  556,  556,  556,
  174.   556,  556,  278,  278,  584,  584,  584,  556,
  175.  1015,  667,  667,  722,  722,  667,  611,  778,
  176.   722,  278,  500,  667,  556,  833,  722,  778,
  177.   667,  778,  722,  667,  611,  722,  667,  944,
  178.   667,  667,  611,  278,  278,  278,  469,  556,
  179.   222,  556,  556,  500,  556,  556,  278,  556,
  180.   556,  222,  222,  500,  222,  833,  556,  556,
  181.   556,  556,  333,  500,  278,  556,  500,  722,
  182.   500,  500,  500,  334,  260,  334,  584,    0  /*,
  183.     0,    0,    0,    0,    0,    0,    0,    0,
  184.     0,    0,    0,    0,    0,    0,    0,    0,
  185.     0,    0,    0,    0,    0,    0,    0,    0,
  186.     0,    0,    0,    0,    0,    0,    0,    0,
  187.     0,  333,  556,  556,  167,  556,  556,  556,
  188.   556,  191,  333,  556,  333,  333,  500,  500,
  189.     0,  556,  556,  556,  278,    0,  537,  350,
  190.   222,  333,  333,  556, 1000, 1000,    0,  611,
  191.     0,  333,  333,  333,  333,  333,  333,  333,
  192.   333,    0,  333,  333,    0,  333,  333,  333,
  193.  1000,    0,    0,    0,    0,    0,    0,    0,
  194.     0,    0,    0,    0,    0,    0,    0,    0,
  195.     0, 1000,    0,  370,    0,    0,    0,    0,
  196.   556,  778, 1000,  365,    0,    0,    0,    0,
  197.     0,  889,    0,    0,    0,  278,    0,    0,
  198.   222,  611,  944,  611,    0,    0,    0,    0  */ },
  199. /* font 2 is Helvetica-Bold */
  200.   {
  201.     0,    0,    0,    0,    0,    0,    0,    0,
  202.     0,    0,    0,    0,    0,    0,    0,    0,
  203.     0,    0,    0,    0,    0,    0,    0,    0,
  204.     0,    0,    0,    0,    0,    0,    0,    0,
  205.   278,  333,  474,  556,  556,  889,  722,  278,
  206.   333,  333,  389,  584,  278,  333,  278,  278,
  207.   556,  556,  556,  556,  556,  556,  556,  556,
  208.   556,  556,  333,  333,  584,  584,  584,  611,
  209.   975,  722,  722,  722,  722,  667,  611,  778,
  210.   722,  278,  556,  722,  611,  833,  722,  778,
  211.   667,  778,  722,  667,  611,  722,  667,  944,
  212.   667,  667,  611,  333,  278,  333,  584,  556,
  213.   278,  556,  611,  556,  611,  556,  333,  611,
  214.   611,  278,  278,  556,  278,  889,  611,  611,
  215.   611,  611,  389,  556,  333,  611,  556,  778,
  216.   556,  556,  500,  389,  280,  389,  584,    0  /*,
  217.     0,    0,    0,    0,    0,    0,    0,    0,
  218.     0,    0,    0,    0,    0,    0,    0,    0,
  219.     0,    0,    0,    0,    0,    0,    0,    0,
  220.     0,    0,    0,    0,    0,    0,    0,    0,
  221.     0,  333,  556,  556,  167,  556,  556,  556,
  222.   556,  238,  500,  556,  333,  333,  611,  611,
  223.     0,  556,  556,  556,  278,    0,  556,  350,
  224.   278,  500,  500,  556, 1000, 1000,    0,  611,
  225.     0,  333,  333,  333,  333,  333,  333,  333,
  226.   333,    0,  333,  333,    0,  333,  333,  333,
  227.  1000,    0,    0,    0,    0,    0,    0,    0,
  228.     0,    0,    0,    0,    0,    0,    0,    0,
  229.     0, 1000,    0,  370,    0,    0,    0,    0,
  230.   611,  778, 1000,  365,    0,    0,    0,    0,
  231.     0,  889,    0,    0,    0,  278,    0,    0,
  232.   278,  611,  944,  611,    0,    0,    0,    0  */ }
  233. };
  234.  
  235. typedef struct a_ps_image_family {
  236.   char *name;
  237.   ImageFamily *imf;
  238.   Image *img;
  239.   char *hexmonodata;
  240.   char *hexmaskdata;
  241. } PsImage;
  242.  
  243. static PsImage *unit_icon, *terr_icon, *side_icon;
  244.  
  245. static int conn_x[NUMDIRS], conn_y[NUMDIRS],
  246.            hexc_x[NUMDIRS+1], hexc_y[NUMDIRS+1];
  247.  
  248. /* Local functions. */
  249.  
  250. static char *add_esc_string PARAMS ((char *str));
  251.  
  252. static void ps_cook_imf PARAMS ((void));
  253. static void ps_initialize_imf PARAMS ((PsImage *psim));
  254. static char *ps_hex_dump PARAMS ((Obj *data, int w, int h));
  255. static char *img_untile PARAMS ((char *tiled, int w, int h, int istile,
  256.              int dither, int scale, int gray));
  257. static void misc_init PARAMS ((void));
  258. static void ps_initialize PARAMS ((FILE *fp));
  259. static void page_init PARAMS ((FILE *fp, int page, int i, int j, char *stime));
  260. static int nearest_valid_x PARAMS ((int x, int y));
  261. static int sideno_of_seen_unit_at PARAMS ((int x, int y, Side *side));
  262. static char *name_of_seen_unit_at PARAMS ((int x, int y, Side *side));
  263. static char *summary_of_seen_units_at PARAMS ((int x, int y, Side *side));
  264. static int ps_string_width PARAMS ((char *str, int font));
  265. static int print_unit_legends PARAMS ((FILE *fp, char *name, char *summary, char *m,
  266.     int dir, int cx, int cy));
  267.  
  268. static void
  269. ps_cook_imf()
  270. {
  271.     int i;
  272.     Side *s;
  273.     Image *img;
  274.     
  275.     if (buffer == NULL)
  276.       buffer = xmalloc(PSBUFSIZE + 4);
  277.     if (unit_icon == NULL)
  278.       unit_icon = (PsImage *)
  279.     xmalloc (numutypes * sizeof(PsImage));
  280.     if (terr_icon == NULL)
  281.       terr_icon = (PsImage *)
  282.     xmalloc (numttypes * sizeof(PsImage));
  283.     if (side_icon == NULL)
  284.       side_icon = (PsImage *)
  285.     xmalloc (MAXSIDES  * sizeof(PsImage));
  286.     
  287.     /* pick the images we need */
  288.     for_all_unit_types(i) {
  289.     unit_icon[i].imf = NULL;
  290.     /* first choice */
  291.     if (utypes[i].imagename) {
  292.         unit_icon[i].name = utypes[i].imagename;
  293.         unit_icon[i].imf = find_imf(utypes[i].imagename);
  294.     }
  295.     /* second choice */
  296.     if (!unit_icon[i].imf && utypes[i].name) {
  297.         unit_icon[i].name = utypes[i].name;
  298.         unit_icon[i].imf = find_imf(utypes[i].name);
  299.     }
  300.  
  301.     if (!unit_icon[i].imf || !unit_icon[i].imf->images) {
  302.         unit_icon[i].img = NULL;
  303.     } else {
  304.         unit_icon[i].img = unit_icon[i].imf->images;
  305.         for (img=unit_icon[i].imf->images->next; img; img=img->next) {
  306.         /* should we prefer the new image? */
  307.         if ((!unit_icon[i].img->monodata && img->monodata) ||
  308.             (!unit_icon[i].img->maskdata && img->maskdata) ||
  309.             ( unit_icon[i].img->w < img->w)) {
  310.             unit_icon[i].img = img;
  311.         }
  312.         }
  313.     }
  314.     ps_initialize_imf(&unit_icon[i]);
  315.     }
  316.     
  317.     for_all_terrain_types(i) {
  318.     terr_icon[i].imf = NULL;
  319.     /* first choice */
  320.     if (ttypes[i].imagename) {
  321.         terr_icon[i].name = ttypes[i].imagename;
  322.         terr_icon[i].imf = find_imf(ttypes[i].imagename);
  323.     }
  324.     /* second choice */
  325.     if (!terr_icon[i].imf && ttypes[i].name) {
  326.         terr_icon[i].name = ttypes[i].name;
  327.         terr_icon[i].imf = find_imf(ttypes[i].name);
  328.     }
  329.     if (terr_icon[i].imf) {
  330.         terr_icon[i].img = terr_icon[i].imf->images;
  331.     } else {
  332.         terr_icon[i].img = NULL;
  333.     }
  334.     ps_initialize_imf(&terr_icon[i]);
  335.     }
  336.     
  337.     i = -1;
  338.     for_all_sides_plus_indep(s) {
  339.     i++;
  340.     side_icon[i].imf = NULL;
  341.     /* first choice */
  342.     if (s->emblemname) {
  343.         side_icon[i].name = s->emblemname;
  344.         side_icon[i].imf = find_imf(s->emblemname);
  345.     }
  346.     if (side_icon[i].imf) {
  347.         side_icon[i].img = side_icon[i].imf->images;
  348.     } else {
  349.         side_icon[i].img = NULL;
  350.     }
  351.     ps_initialize_imf(&side_icon[i]);
  352.     }
  353.     
  354.     img_cooked = 1;
  355. }
  356.  
  357. static void 
  358. ps_initialize_imf(psim)
  359. PsImage *psim;
  360. {
  361.     /* checks & cleanup */
  362.     if (!psim)
  363.       return;
  364.     if (!psim->imf)
  365.       psim->img = NULL;
  366.  
  367.     if (!psim->img) {
  368.     psim->hexmonodata = NULL;
  369.     psim->hexmaskdata = NULL;
  370.     return;
  371.     }
  372.     
  373.     psim->hexmonodata =
  374.       ps_hex_dump(psim->img->monodata, psim->img->w, psim->img->h);
  375.     psim->hexmaskdata =
  376.       ps_hex_dump(psim->img->maskdata, psim->img->w, psim->img->h);
  377. }
  378.  
  379. static char *
  380. ps_hex_dump(data, w, h)
  381. Obj *data;
  382. int w, h;
  383. {
  384.     int i, numbytes;
  385.     unsigned int u;
  386.     char *hexdata, *c;
  387.   
  388.     numbytes = h * ((w + 7) / 8);
  389.     interp_bytes(data, numbytes, buffer, 0);
  390.     c = hexdata = xmalloc(2*numbytes+2);
  391.     for (i = 0; i < numbytes; ++i) {
  392.     u = buffer[i] & 0xff;
  393.     sprintf(c, "%2.2x", u);
  394.     c += 2;
  395.     }
  396.     *c = '\0';
  397.     return hexdata;
  398. }
  399.  
  400. /* Should use two symbolic constants instead of 8 ? */
  401.  
  402. static char *
  403. img_untile(tiled, w, h, istile, dither, scale, gray)
  404. char *tiled;
  405. int w, h, istile, dither, scale, gray;
  406. {
  407.     int dx, dy, x, y, xo, yo, xi, yi, tw, th;
  408.     unsigned int u;
  409.     char mask[8][8], str[12], *b;
  410.  
  411.     if (tiled == NULL)
  412.       return zero;
  413.     if (!istile)
  414.       return tiled;
  415.  
  416.     if (strlen(tiled) != area_leng(8,8))
  417.       return zero;
  418.   
  419.     /* untile image into hexagon */
  420.  
  421.     /* width and height of the four triangles we have to cut off
  422.        a hex_w*hex_h rectangle to form a hexagon */
  423.     tw = hex_w / 2;
  424.     th = hex_h - hex_c;
  425.  
  426.     /* convert to binary */
  427.     b = tiled;
  428.     for (y = 0; y < 8; y++) {
  429.     for (x = 0; x < 8; x++) {
  430.         if ((x & 0x3) == 0) {
  431.         str[0] = *b++;
  432.         str[1] = '\0';
  433.         sscanf(str, "%x", &u);
  434.         }
  435.         mask[x][y] = (u & 0x8) >> 3;
  436.         u = u << 1;
  437.     }
  438.     }
  439.  
  440.     if (area_leng(scale * hex_w, scale * hex_h) > PSBUFSIZE) {
  441.     notify(ps_side, "buffer size is %d, too small to load %dx%d image",
  442.            PSBUFSIZE, scale * hex_w, scale * hex_h);
  443.     return zero;
  444.     }
  445.  
  446.     /* Convert to hexadecimal. */
  447.     b = buffer;
  448.     for (yo = 0, y = 0; yo < hex_h; yo++) {
  449.     dy = min(yo, hex_h - 1 - yo);
  450.     for (yi = 0; yi < scale; yi++, y++) {
  451.         u = 0;
  452.         for (xo = 0, x = 0; xo < hex_w; xo++) {
  453.         dx = min(xo, hex_w - 1 - xo);
  454.         for (xi = 0; xi < scale; xi++, x++) {
  455.             u = u << 1;
  456.             /* is the center of the (xo,yo) cell inside the hexagon? */
  457.             if (th * dx + tw * dy >= th * tw - (th + tw) / 2) {
  458.             u |= mask[x & 0x7][y & 0x7]
  459.               && xrandom(GRAY_SCALE) >= gray;
  460.             }
  461.             if ((x & 0x3) == 0x3 || x == scale * hex_w - 1) {
  462.             sprintf(b++, "%1.1x", u);
  463.             u = 0;
  464.             }
  465.         }
  466.         }
  467.         /* does the hexadecimal string need padding? */
  468.         if (((scale * hex_w - 1) & 7) < 4) {
  469.         *b++ = '0';
  470.         }
  471.     }
  472.     }
  473.  
  474.     *b = '\0';
  475.     if (strlen(buffer) != area_leng(scale * hex_w, scale * hex_h)) {
  476.     notify(ps_side, "length (%s) is %d, expecting %d",
  477.            buffer, strlen(buffer),
  478.            area_leng(scale * hex_w, scale * hex_h));
  479.     }
  480.     return buffer;
  481. }
  482.  
  483. /* Convert to PostScript escape sequences:
  484.    '\' to '\\', '(' to '\(', ')' to '\)'.  */
  485.  
  486. static char *
  487. add_esc_string(str)
  488. char *str;
  489. {
  490.     char *ps, *pb;
  491.  
  492.     if (escbuf == NULL)
  493.       escbuf = xmalloc(BUFSIZE);
  494.     for (ps = str, pb = escbuf; *ps && pb < escbuf + BUFSIZE - 2; ps++, pb++) {
  495.     if (*ps == '(' || *ps == ')' || *ps == '\\')
  496.       *pb++ = '\\';
  497.     *pb = *ps;
  498.     }
  499.     *pb = '\0';
  500.     return escbuf;
  501. }
  502.  
  503. /* set parameter defaults */
  504. /* all length are in PostScript units (1/72 in)  */
  505.  
  506. void
  507. init_ps_print(ipp)
  508. PrintParameters *ipp;
  509. {
  510.     double in, cm;
  511.     
  512.     if (pp == NULL) {
  513.     pp = (PrintParameters *) xmalloc(sizeof(PrintParameters));
  514.  
  515.     /* the following code must be executed once,
  516.        before calling the parameter selection front-end */
  517.     in = 72.0;
  518.     cm = in/2.54;
  519.  
  520.     /*  US, letter sheet  */
  521.     pp->page_width =    8.5*in;
  522.     pp->page_height =  11.0*in;
  523.     pp->cm = 0;        /* use inches in front-end */
  524.  
  525.     /*  metric world, A4 sheet  */
  526.     pp->page_width =   21.0*cm;
  527.     pp->page_height =  29.7*cm;
  528.     pp->cm = 1;        /* use centimeters in front-end */
  529.  
  530.     /*  title and coordinates are printed in the "margins":
  531.         make room in top_margin, left_margin and right_margin  */
  532.     pp->top_margin =    3.0*cm;
  533.     pp->bottom_margin = 2.0*cm;
  534.     pp->left_margin =   2.0*cm;
  535.     pp->right_margin =  2.0*cm;
  536.  
  537.     pp->cell_size        = 0.500*cm;
  538.     pp->cell_grid_width  = 0.005*cm; /* 0 or negative to avoid hex borders */
  539.     pp->border_width     = 0.050*cm;
  540.     pp->connection_width = 0.025*cm;
  541.  
  542.     pp->features  =  1;    /* print geographical features */
  543.  
  544.     /*  names = 0:  don't print unit names;
  545.         names = 1:  filter unit names;
  546.         names = 2:  print all unit names.  */
  547.     pp->names = 1;
  548.     pp->cell_summary = 1;    /* print summary of units in cell */
  549.     pp->corner_coord   = 1;    /* print coordinates of map corners */
  550.     pp->terrain_gray   = 0.50; /* gray level for terrain;  0 = black */
  551.     pp->enemy_gray     = 0.50; /* gray level for enemies;  0 = black */
  552.     pp->terrain_dither = 0;    /* make terrain gray by dithering;
  553.                    0 = use PostScript "setgray" operator */
  554.     pp->terrain_double = 1;    /* make mesh finer when dithering */
  555.     }
  556.     
  557.     if (ipp) {
  558.     *ipp = *pp;
  559.     }
  560. }
  561.  
  562. /* initialization of variables */
  563.  
  564. static void 
  565. misc_init()
  566. {
  567.     if (pp->terrain_dither) {
  568.     terr_gray = GRAY_SCALE*pp->terrain_gray + 0.5;
  569.     terr_scale = (pp->terrain_double && terr_gray>0 &&
  570.               terr_gray<GRAY_SCALE) ? 2 : 1;
  571.     } else {
  572.     terr_gray = 0;
  573.     terr_scale = 1;
  574.     }
  575.     ru = 1.0 / unit_w;
  576.     rtw = 1.0 / hex_w;
  577.     rth = 1.0 / hex_h;
  578.     sqrt3 = 1.73205080756888;    /* sqrt(3.0);  hardwired to avoid need of -lm */
  579.     asym = hex_c / (0.5 * sqrt3 * hex_w);
  580.     
  581.     x0 = area.xwrap ? 0 : (area.height+2)/4;
  582.     
  583.     /* from a hex center to neighbor hex centers */
  584.     conn_x[0] =  hex_w/2;        conn_y[0] =  hex_c;
  585.     conn_x[1] =  hex_w;        conn_y[1] =  0;
  586.     conn_x[2] =  hex_w/2;        conn_y[2] = -hex_c;
  587.     conn_x[3] = -hex_w/2;        conn_y[3] = -hex_c;
  588.     conn_x[4] = -hex_w;        conn_y[4] =  0;
  589.     conn_x[5] = -hex_w/2;        conn_y[5] =  hex_c;
  590.     
  591.     /* from a hex center to hex vertices (with wraparound) */
  592.     hexc_x[0] =  0;        hexc_y[0] =  hex_h / 2;
  593.     hexc_x[1] =  hex_w/2;        hexc_y[1] =  hex_h/4;
  594.     hexc_x[2] =  hex_w/2;        hexc_y[2] = -hex_h/4;
  595.     hexc_x[3] =  0;        hexc_y[3] = -hex_h / 2;
  596.     hexc_x[4] = -hex_w/2;        hexc_y[4] = -hex_h/4;
  597.     hexc_x[5] = -hex_w/2;        hexc_y[5] =  hex_h/4;
  598.     hexc_x[6] =  0;        hexc_y[6] =  hex_h / 2;
  599.     
  600.     cell_grid_width  = pp->cell_grid_width  * hex_w/pp->cell_size;
  601.     border_width     = pp->border_width     * hex_w/pp->cell_size;
  602.     connection_width = pp->connection_width * hex_w/pp->cell_size;
  603.     
  604.     /* rows per page and cells per row */
  605.     height = (pp->page_height - pp->top_margin - pp->bottom_margin)
  606.       * 2 / (sqrt3 * pp->cell_size);
  607.     width  = (pp->page_width - pp->left_margin - pp->right_margin)
  608.       / pp->cell_size;
  609. }
  610.  
  611. /* Write prolog and macro definitions to view file. */
  612.  
  613. static void
  614. ps_initialize(fp)
  615. FILE *fp;
  616. {
  617.     int i;
  618.     Image *img;
  619.     Side *s;
  620.     
  621.     /*  PostScript prolog  */
  622.     fprintf(fp, "%%!PS-Adobe-2.0\n");
  623.     fprintf(fp, "%%%%BoundingBox: %d %d %d %d\n",
  624.         (int) pp->left_margin,
  625.         (int) pp->top_margin,
  626.         (int) (pp->page_width - pp->left_margin),
  627.         (int) (pp->page_height - pp->top_margin));
  628.     fprintf(fp, "%%%%Creator: Xconq version %s\n", version_string());
  629.     
  630. #ifdef UNIX
  631.     {
  632.     char *userp, *hostp;
  633.  
  634.     userp = getenv("USER");
  635.     hostp = getenv("HOST");
  636.     if (userp != 0) {
  637.         if (hostp != 0) {
  638.         fprintf(fp, "%%%%For: %s@%s\n", userp, hostp);
  639.         } else {
  640.         fprintf(fp, "%%%%For: %s\n", userp);
  641.         }
  642.     }
  643.     }
  644. #endif /* UNIX */
  645. #ifdef MAC
  646.     /* ??? */
  647. #endif /* MAC */
  648.     
  649.     fprintf(fp, "%%%%DocumentFonts:\
  650.  Helvetica Helvetica-Narrow Helvetica-Bold Times-Bold\n");
  651.     fprintf(fp, "%%%%Pages: (atend)\n");
  652.     fprintf(fp, "%%%%EndComments\n");
  653.     
  654.     /* PostScript definitions */
  655.     
  656.     /* new line */
  657.     fprintf(fp, "/nl { 0 %d translate } bind def\n", -hex_c);
  658.     /* backspace */
  659.     fprintf(fp, "/bs { %d 0 translate } bind def\n", -hex_w);
  660.     /* space */
  661.     fprintf(fp, "/sp { %d 0 translate } bind def\n", hex_w);
  662.     /* multiple spaces */
  663.     fprintf(fp, "/msp { %d mul 0 translate } bind def\n", hex_w);
  664.     /* half space */
  665.     fprintf(fp, "/hs { %d 0 translate } bind def\n", hex_w / 2);
  666.     /* show string right of current position */
  667.     fprintf(fp, "/rshow { show } bind def\n");
  668.     /* show string centered on current position */
  669.     fprintf(fp, "/cshow { dup stringwidth pop -0.5 mul 0 rmoveto");
  670.     fprintf(fp, " show } bind def\n");
  671.     /* show string left of current position, correcting asymmetry */
  672.     fprintf(fp, "/lshow { dup stringwidth pop neg 0 rmoveto");
  673.     fprintf(fp, " show } bind def\n");
  674.     /* draw hexagon (if requested) */
  675.     if (cell_grid_width > 0) {
  676.     fprintf(fp, "/hex { currentgray 0 setgray 0 %d moveto %d 0 lineto %d %d",
  677.         hex_c-hex_h, hex_w/2, hex_w,hex_c-hex_h);
  678.     fprintf(fp, " lineto %d %d lineto %d %d lineto 0 %d lineto 0 %d lineto",
  679.         hex_w,-hex_c, hex_w/2,-hex_h, -hex_c, hex_c-hex_h);
  680.     fprintf(fp, " stroke setgray } bind def\n");
  681.     } else {
  682.     fprintf(fp, "/hex { } bind def\n");
  683.     }
  684.     /* draw a line */
  685.     fprintf(fp, "/li { moveto lineto stroke } bind def\n");
  686.     
  687.     /* print unit name below unit icon */
  688.     fprintf(fp, "/n { %d %d moveto sbc } bind def\n", hex_w / 2, 1-hex_h);
  689.     
  690.     /* print left-justified in upper section of hex  */
  691.     fprintf(fp, "/nlu { 0 %d moveto sbl } bind def\n", -hex_h / 2);
  692.     /* print left-justified in lower section of hex  */
  693.     fprintf(fp, "/nld { 0 %d moveto sbl } bind def\n", -(hex_h+hex_c) / 2);
  694.     /* print right-justified in upper section of hex  */
  695.     fprintf(fp, "/nru { %d %d moveto sbr } bind def\n", hex_w, -hex_h / 2);
  696.     /* print right-justified in lower corner of hex  */
  697.     fprintf(fp, "/nrd { %d %d moveto sbr } bind def\n",
  698.         hex_w, -(hex_h+hex_c) / 2);
  699.     
  700.     /* print a centered string, blanking out a bounding rectangle */
  701.     fprintf(fp, "/sbc { dup stringwidth gsave ");
  702.     fprintf(fp, "0 -0.5 rmoveto pop dup 0.5 add 7 scale -0.5 0 rmoveto ");
  703.     fprintf(fp,
  704.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  705.     fprintf(fp, "grestore -0.5 mul 0.5 rmoveto show } bind def\n");
  706.     /* print a left-aligned string, blanking out a bounding rectangle */
  707.     fprintf(fp, "/sbl { dup stringwidth gsave ");
  708.     fprintf(fp, "0 -0.5 rmoveto pop 0.5 add 7 scale ");
  709.     fprintf(fp,
  710.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  711.     fprintf(fp, "grestore 0 0.5 rmoveto show } bind def\n");
  712.     /* print a right-aligned string, blanking out a bounding rectangle */
  713.     fprintf(fp, "/sbr { dup stringwidth gsave ");
  714.     fprintf(fp, "0 -0.5 rmoveto pop dup 0.5 add 7 scale -1 0 rmoveto ");
  715.     fprintf(fp,
  716.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  717.     fprintf(fp, "grestore neg 0.5 rmoveto show } bind def\n");
  718.     
  719.     /* draw a unit */
  720.     for_all_unit_types(i) {
  721.     if (!unit_icon[i].hexmonodata) {
  722.         fprintf(fp, "%% icon for unit type %s not found\n",
  723.             utypes[i].name);
  724.         fprintf(fp, "/u%X { } def\n", i);
  725.     } else {
  726.         img = unit_icon[i].img;
  727.         fprintf(fp, "%% unit type %s\n", utypes[i].name);
  728.         /* center unit icon into hex */
  729.         fprintf(fp, "/u%X { gsave %d %d translate",
  730.             i, (hex_w - unit_w) / 2, - (hex_h - unit_w) / 2 - 3);
  731.         if (unit_icon[i].hexmaskdata) {
  732.         fprintf(fp, " 1 setgray %d %d true [ %g 0 0 %g 0 0 ]",
  733.             img->w, img->h, img->w * ru, - img->h * ru);
  734.         fprintf(fp, " {<%s>} imagemask",
  735.             unit_icon[i].hexmaskdata);
  736.         }
  737.         fprintf(fp, " 0 setgray %d %d true [ %g 0 0 %g 0 0 ]",
  738.             img->w, img->h, img->w * ru, - img->h * ru);
  739.         fprintf(fp, " {<%s>} imagemask grestore } def\n",
  740.             unit_icon[i].hexmonodata);
  741.     }
  742.     }
  743.     
  744.     /* draw a terrain call and a hex */
  745.     for_all_terrain_types(i) {
  746.     if (!terr_icon[i].hexmonodata) {
  747.         fprintf(fp, "%% icon for terrain type %s not found\n",
  748.             ttypes[i].name);
  749.         fprintf(fp, "/t%X { hex sp } def\n", i);
  750.     } else {
  751.         int w, h;
  752.  
  753.         img = terr_icon[i].img;
  754.         w = img->w;
  755.         h = img->h;
  756.         if (img->istile && w==8 && h==8) {
  757.         w = terr_scale*hex_w;  h = terr_scale*hex_h;
  758.         }
  759.         fprintf(fp, "%% terrain type %s\n", ttypes[i].name);
  760.         fprintf(fp, "/t%X { %d %d true [ %g 0 0 %g 0 0 ]",
  761.             i, w, h, w*rtw, -h*rth);
  762.         fprintf(fp, " {<%s>} imagemask hex sp } def\n",
  763.             img_untile(terr_icon[i].hexmonodata, w, h, img->istile,
  764.                    pp->terrain_dither, terr_scale, terr_gray));
  765.     }
  766.     }
  767.     
  768.     /* draw an emblem */
  769.     i = 0;
  770.     for_all_sides_plus_indep(s) {
  771.     if (!side_icon[i].hexmonodata) {
  772.         fprintf(fp, "%% icon for side %s not found\n", side_name(s));
  773.         fprintf(fp, "/s%X { } def\n", side_number(s));
  774.     } else {
  775.         img = side_icon[i].img;
  776.         fprintf(fp, "%% side %s\n", side_name(s));
  777.         /* place side icon into upper corner of hex */
  778.         fprintf(fp, "/s%X { gsave %g %g translate",
  779.             side_number(s), 0.5 * (hex_w - img->w), -4 / asym);
  780.         if (side_icon[i].hexmaskdata) {
  781.         fprintf(fp, " 1 setgray %d %d true [ 1 0 0 -1 0 0 ]", img->w, img->h);
  782.         fprintf(fp, " {<%s>} imagemask",
  783.             side_icon[i].hexmaskdata);
  784.         }
  785.         fprintf(fp, " 0 setgray %d %d true [ 1 0 0 -1 0 0 ]", img->w, img->h);
  786.         fprintf(fp, " {<%s>} imagemask grestore } def\n",
  787.             side_icon[i].hexmonodata);
  788.     }
  789.     i++;
  790.     }
  791.     
  792.     fprintf(fp, "%%%%EndProlog\n");
  793.     fprintf(fp, "\n");
  794. }
  795.  
  796. /* Initialize a page. */
  797.  
  798. static void 
  799. page_init(fp, page, i, j, stime)
  800. FILE *fp;
  801. int page, i, j;
  802. char *stime;
  803. {
  804.     int x, xx, y, y0, nx, ny;
  805.     char *gametitle;
  806.     
  807.     fprintf(fp, "%%%%Page: %d %d\n", page, page);
  808.     fprintf(fp, "%g %g translate ",
  809.         pp->left_margin, pp->page_height - pp->top_margin);
  810.     fprintf(fp, "%g %g scale\n", pp->cell_size / hex_w, pp->cell_size / hex_w);
  811.     
  812.     /* print page title */
  813.     gametitle = NULL;
  814.     if (mainmodule != NULL) {
  815.     if (mainmodule->title != NULL)
  816.       gametitle = mainmodule->title;
  817.     else if (mainmodule->name != NULL)
  818.       gametitle = mainmodule->name;
  819.     }
  820.     
  821.     /* "20" shouldn't be wired in... */
  822.     fprintf(fp, "/Times-Bold findfont 16 scalefont setfont\n");
  823.     
  824.     fprintf(fp, "%g 20 moveto (%s) rshow\n", 0.05 * width * hex_w,
  825.         add_esc_string(stime));
  826.     
  827.     fprintf(fp, "%g 20 moveto (Xconq", 0.5 * width * hex_w);
  828.     if (gametitle != NULL) {
  829.     fprintf(fp, ":  %s", add_esc_string(gametitle));
  830.     }
  831.     fprintf(fp, "  \\(%s\\)) cshow\n",
  832.         add_esc_string(absolute_date_string(g_turn())));
  833.     
  834.     fprintf(fp, "%g 20 moveto (Page %d) lshow\n", 0.95 * width * hex_w, page);
  835.     
  836.     /*  print map corner coordinates? */
  837.     if (pp->corner_coord) {
  838.     fprintf(fp, "/Helvetica findfont 12 scalefont setfont\n");
  839.     
  840.     y0 = y = area.height - 1 - i * height;
  841.     x = j * width - (y+1) / 2 + x0;
  842.     x = wrapx(x);
  843.     xx = nearest_valid_x(x, y) - x;
  844.     x += xx;
  845.     fprintf(fp, "%d %g moveto (%d,%d) lshow\n",
  846.         (2 * xx - 1) * hex_w / 2, 0 / asym, x, y);
  847.     
  848.     nx = min(width, area.width-j*width);
  849.     x = j * width + nx - 1 - (y + 1) / 2 + x0;
  850.     x = wrapx(x);
  851.     xx = nearest_valid_x(x, y) - x;
  852.     x += xx;
  853.     fprintf(fp, "%d %g moveto (%d,%d) rshow\n",
  854.         (2 * (nx + xx) + 1) * hex_w / 2, 0 / asym, x, y);
  855.     
  856.     y = max(area.height - (i + 1) * height, 0);
  857.     ny = y0 - y;
  858.     x = j * width - (y + 1) / 2 + x0;
  859.     x = wrapx(x);
  860.     xx = nearest_valid_x(x, y) - x;
  861.     x += xx;
  862.     fprintf(fp, "%d %g moveto (%d,%d) lshow\n",
  863.         (2 * xx - 1) * hex_w / 2, -(hex_c * (ny + 2) + 3) / asym,
  864.         x, y);
  865.     
  866.     x = j * width + nx - 1 - (y + 1) / 2 + x0;
  867.     x = wrapx(x);
  868.     xx = nearest_valid_x(x, y) - x;
  869.     x += xx;
  870.     fprintf(fp, "%d %g moveto (%d,%d) rshow\n",
  871.         (2 * (nx + xx) + 1) * hex_w / 2,
  872.         -(hex_c * (ny + 2) +3 ) / asym, x, y);
  873.     }
  874. }
  875.  
  876. static int
  877. nearest_valid_x(x, y)
  878. int x, y;
  879. {
  880.     int xlo, xhi;
  881.  
  882.     if (area.xwrap)
  883.       return x;
  884.     xlo = max(0, area.halfheight - y);
  885.     xhi = min(area.width-1, area.width + area.halfheight - 1 - y);
  886.     x = max(x, xlo);
  887.     x = min(x, xhi);
  888.     return x;
  889. }
  890.  
  891. static int
  892. sideno_of_seen_unit_at(x, y, side)
  893. int x, y;
  894. Side *side;
  895. {
  896.    Unit *unit;
  897.    short view;
  898.  
  899.    if (!in_area(x, y))
  900.      return indepside->id;
  901.    unit = unit_seen_at(side, x, y);
  902.    if (unit)
  903.      return side_number(unit->side);
  904.    view = unit_view(side, x, y);
  905.    if (view != EMPTY)
  906.      return vside(view);
  907.    return indepside->id;
  908. }
  909.  
  910. static char *
  911. name_of_seen_unit_at(x, y, side)
  912. int x, y;
  913. Side *side;
  914. {
  915.     Unit *unit;
  916.     
  917.     if (!pp->names || !in_area(x, y))
  918.       return NULL;
  919.     unit = unit_seen_at(side, x, y);
  920.     if (unit) {
  921.     if (pp->names > 1) {
  922.         name_or_number(unit, buffer);
  923.         if (buffer[0]) {
  924.         return add_esc_string(buffer);
  925.         }
  926.     } else {
  927.         if (unit->name && unit->name[0]) {
  928.         return add_esc_string(unit->name);
  929.         } 
  930.     }
  931.     } 
  932.     return NULL;
  933. }
  934.  
  935. static char *
  936. summary_of_seen_units_at(x, y, side)
  937. int x, y;
  938. Side *side;
  939. {
  940.     Unit *mainunit, *other, *occ;
  941.     int i, nums[MAXUTYPES];
  942.     
  943.     if (!in_area(x, y) || !pp->cell_summary)
  944.       return NULL;
  945.     mainunit = unit_seen_at(side, x, y);
  946.     if (!mainunit || (!mainunit->nexthere && !mainunit->occupant))
  947.       return NULL;
  948.     
  949.     for_all_unit_types(i) {
  950.     nums[i] = 0;
  951.     }
  952.     for_all_stack(x, y, other) {
  953.     nums[other->type]++;
  954.     for_all_occupants(other, occ) {
  955.         nums[occ->type]++;
  956.     }
  957.     }
  958.     nums[mainunit->type]--;
  959.     
  960.     strcpy(buffer, "(+");
  961.     for_all_unit_types(i) {
  962.     if (nums[i] != 0) {
  963.         tprintf(buffer, " %d %1s,", nums[i], utype_name_n(i, 1));
  964.     }
  965.     }
  966.     i = strlen(buffer);
  967.     buffer[i-1] = ')';
  968.     
  969.     return add_esc_string(buffer);
  970. }
  971.  
  972. /* This routine could still use a cleanup */
  973.  
  974. void
  975. dump_ps_view(side, ipp, filename)
  976. Side *side;
  977. PrintParameters *ipp;
  978. char *filename;
  979. {
  980.     int x, y, xx, i, j, t, nx, utype, page, d, cx, cy, skip, nsp;
  981.     int f, nf, tw, th, font, named_unit_max=100, xmin, ymin, xmin1;
  982.     int l, l1, x1, y1, x2, y2, d1, mvalue, nvalue, pad;
  983.     FILE *fp;
  984.     char stime[42], *name, *summary, tmpbuf[BUFSIZE];
  985.     char *name_layer, *name_position;
  986.     time_t tnow;
  987.     Legend *legs = NULL, *lp;
  988.     double length, magnif;
  989.     
  990.     if (ipp) {
  991.     *pp = *ipp;
  992.     }
  993.     
  994.     ps_side = (side) ? side : sidelist->next;
  995.     misc_init();
  996.     
  997.     time(&tnow);
  998.     strftime(stime, 40, "%d %b %Y", localtime(&tnow));
  999.     
  1000.     /* Compute position of legends of geographical features. */
  1001.     if (pp->features) {
  1002.     nf = num_features();
  1003.     if (nf > 0) {
  1004.         legs = (Legend *) malloc((nf + 1) * sizeof(Legend));
  1005.         if (legs)
  1006.           place_feature_legends(legs, nf, ps_side, 2, 1);
  1007.     }
  1008.     }
  1009.     
  1010.     /* need to initialize and cook the image library? */
  1011.     if (img_cooked == 0) {
  1012.     init_ps_print(NULL);
  1013.     ps_cook_imf();
  1014.     }
  1015.     if (img_cooked != 1) {
  1016.     notify(ps_side, "image cooking failed, dump skipped");
  1017.     return;
  1018.     }
  1019.     
  1020.     /*  open view file  */
  1021.     fp = fopen(filename, "w");
  1022.     if (fp == NULL) {
  1023.     notify(ps_side, "Cannot open \"%s\", dump skipped", filename);
  1024.     return;
  1025.     }
  1026.     
  1027.     ps_initialize(fp);
  1028.     
  1029.     name_layer = (char *) malloc(((height+2) * (width+2) + 2) * sizeof(char));
  1030.     name_position = (char *) malloc((named_unit_max + 2) * sizeof(char));
  1031.     
  1032.     /* Loop over pages. */
  1033.     page = 0;
  1034.     for (i = 0; i < (area.height + height - 1) / height; i++) {
  1035.     for (j = 0; j < (area.width + width - 1) / width; j++) {
  1036.         xmin = j * width;  ymin = max(area.height - (i+1) * height, 0);
  1037.  
  1038.         /* See if the page can be skipped because none it has been seen yet. */
  1039.         skip = TRUE;
  1040.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1041.         nx = min(width, area.width - xmin);
  1042.         for (xx = xmin; xx < xmin + nx; xx++) {
  1043.             x = xx - (y + 1) / 2 + x0;
  1044.             x = wrapx(x);
  1045.  
  1046.             if ((terrain_seen_at(side, x, y) != NONTTYPE)
  1047.             || (utype_seen_at(side, x, y) != NONUTYPE)) {
  1048.             skip = FALSE;
  1049.             break;
  1050.             }
  1051.         }
  1052.         if (!skip)
  1053.           break;
  1054.         }
  1055.         if (skip)
  1056.           continue;
  1057.  
  1058.         ++page;
  1059.         page_init(fp, page, i, j, stime);
  1060.  
  1061.         /* show terrain, asymmetric scale to make perfectly
  1062.            regular hexagons  */
  1063.  
  1064.         fprintf(fp, "%g setlinewidth gsave 1 %g scale %g setgray\n",
  1065.             cell_grid_width, 1 / asym,
  1066.             (pp->terrain_dither ? 0.0 : pp->terrain_gray));
  1067.  
  1068.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1069.         fprintf(fp, "gsave ");
  1070.         if ((y % 2) == 0)
  1071.           fprintf(fp, "hs ");
  1072.         nx = min(width, area.width - xmin);
  1073.         nsp = 0;
  1074.         for (xx = xmin; xx < xmin + nx; xx++) {
  1075.             x = xx - (y + 1) / 2 + x0;
  1076.             x = wrapx(x);
  1077.  
  1078.             if ((t = terrain_seen_at(side, x, y)) != NONTTYPE) {
  1079.             if (nsp > 1) {
  1080.                 fprintf(fp, "%d msp ", nsp);
  1081.             } else if (nsp == 1) {
  1082.                 fprintf(fp, "sp ");
  1083.             }
  1084.             nsp = 0;
  1085.             fprintf(fp, "t%X ", t);
  1086.             } else {
  1087.             nsp++;
  1088.             }
  1089.         }
  1090.         fprintf(fp, "grestore nl\n");
  1091.         }
  1092.         fprintf(fp, "grestore\n");
  1093.  
  1094.         /* draw borders.  (cx,cy) is the hex center */
  1095.  
  1096.         fprintf(fp, "gsave %g setlinewidth 1 setlinecap\n", border_width);
  1097.  
  1098.         cy = -hex_h / 2 + hex_c;
  1099.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1100.         cy -= hex_c;
  1101.  
  1102.         cx = (1 + ((y%2)==0))*hex_w/2 - hex_w;
  1103.         nx = min(width, area.width-xmin);
  1104.         for (xx = xmin; xx < xmin + nx; xx++) {
  1105.             cx += hex_w;
  1106.             x = xx - (y + 1) / 2 + x0;
  1107.             x = wrapx(x);
  1108.             if (!in_area(x, y))
  1109.               continue;
  1110.  
  1111.             for_all_terrain_types(t) {
  1112.             if (t_is_border(t) && aux_terrain_defined(t)) {
  1113.                 for_all_directions(d) {
  1114.                 if (border_at(x, y, d, t) &&
  1115.                     (!side
  1116.                      || all_see_all
  1117.                      || seen_border(side, x, y, d))) {
  1118.                     fprintf(fp, "%d %g %d %g li\n",
  1119.                         cx + hexc_x[d],
  1120.                         (cy + hexc_y[d]) / asym,
  1121.                         cx + hexc_x[d + 1],
  1122.                         (cy + hexc_y[d + 1]) / asym);
  1123.                 }
  1124.                 }
  1125.             }
  1126.             }
  1127.         }
  1128.         }
  1129.         fprintf(fp, "grestore\n");
  1130.  
  1131.         /* draw connections.  (cx,cy) is the hex center */
  1132.  
  1133.         fprintf(fp, "gsave %g setlinewidth 1 setlinecap\n",
  1134.             connection_width);
  1135.  
  1136.         cy = -hex_h / 2 + hex_c;
  1137.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1138.         cy -= hex_c;
  1139.         cx = (1 + ((y % 2) == 0)) * hex_w / 2 - hex_w;
  1140.         nx = min(width, area.width - xmin);
  1141.         for (xx = xmin; xx < xmin + nx; xx++) {
  1142.             cx += hex_w;
  1143.             x = xx - (y + 1) / 2 + x0;
  1144.             x = wrapx(x);
  1145.             if (!in_area(x, y))
  1146.               continue;
  1147.             for_all_terrain_types(t) {
  1148.             if (t_is_connection(t) && aux_terrain_defined(t)) {
  1149.                 for_all_directions(d) {
  1150.                 if (connection_at(x, y, d, t) &&
  1151.                     (!side || all_see_all ||
  1152.                      terrain_view(side, x, y) != UNSEEN)) {
  1153.                     fprintf(fp, "%d %g %d %g li\n",
  1154.                         cx,
  1155.                         cy / asym,
  1156.                         cx + conn_x[d],
  1157.                         (cy + conn_y[d]) / asym);
  1158.                 }
  1159.                 }
  1160.             }
  1161.             }
  1162.         }
  1163.         }
  1164.         fprintf(fp, "grestore\n");
  1165.  
  1166.         /* print feature names.  (cx,cy) is the legend center */
  1167.  
  1168.         if (pp->features && legs) {
  1169.         fprintf(fp, "gsave\n");
  1170.         th = -hex_c * (area.height - i * height - 1
  1171.                    - max(area.height - (i+1) * height,0))  - hex_h;
  1172.         tw = hex_w * min(width, area.width - xmin) + hex_w / 2;
  1173.         fprintf(fp, "newpath 0 0 moveto 0 %g lineto %d %g lineto ",
  1174.             th / asym, tw, th / asym);
  1175.         fprintf(fp, "%d 0 lineto 0 0 lineto closepath clip\n", tw);
  1176.         
  1177.         for (f = 1, lp = legs; f <= nf; f++, lp++) {
  1178.             name = feature_desc(find_feature(f), buffer);
  1179.             if (lp->dist < 0.0 || !name)
  1180.               continue;
  1181.             xx = wrapx(lp->ox + (lp->oy + 1) / 2 - x0) - xmin;
  1182.             if (xx >= nx) {
  1183.             xx -= area.width;
  1184.             }
  1185.             cx = (1 + ((lp->oy%2)==0))*hex_w/2 +
  1186.               (2*lp->dx+lp->dy)*hex_w/4 + xx*hex_w;
  1187.             cy = -hex_h / 2 - hex_c * (area.height - 1 - i * height - lp->oy) +
  1188.               lp->dy * hex_c / 2;
  1189.  
  1190.             length = hex_w * (lp->dist + 1);
  1191.             magnif = (1000 * length) / ps_string_width(name, 2);
  1192.  
  1193.             /* tweak these parameters: */
  1194.             magnif *= 0.9;
  1195.             if (magnif < 6) {
  1196.             magnif = 8;
  1197.             font = 0;
  1198.             } else if (magnif < 8) {
  1199.             magnif = 8;
  1200.             font = 1;
  1201.             } else if (magnif < 20) {
  1202.             font = 2;
  1203.             } else {
  1204.             /* keep magnif near 20 */
  1205.             font = 2;
  1206.             pad = ps_string_width(name, 2)
  1207.               * (magnif / 20.0 - 1.0)
  1208.                 / (strlen(name) * ps_string_width(" ", 2)) + 0.5;
  1209.             name = pad_blanks(name, pad);
  1210.             magnif =
  1211.               (1000 * length) / ps_string_width(name, 2) * 0.9;
  1212.             }
  1213.  
  1214.             /* comment containing debugging info */
  1215.             fprintf(fp, "%% f = %d, ox = %d, oy = %d, dx = %d, dy = %d, ",
  1216.                 f, lp->ox, lp->oy, lp->dx, lp->dy);
  1217.             fprintf(fp, "angle = %g, dist = %g\n",
  1218.                 lp->angle, lp->dist);
  1219.             fprintf(fp, "/%s findfont 1 scalefont setfont ",
  1220.                 ps_name[font]);
  1221.             fprintf(fp, "%d %g moveto gsave %g rotate %g %g scale ",
  1222.                 cx, cy/asym, lp->angle, magnif, magnif);
  1223.             fprintf(fp, "0 %g rmoveto (%s) cshow grestore\n",
  1224.                 -0.0005*ps_height, add_esc_string(name));
  1225.         }
  1226.         fprintf(fp, "grestore\n");
  1227.         }
  1228.  
  1229.         /* look for name/summary places */
  1230.         if (name_layer && name_position) {
  1231.         for (l = 0; l < (height + 2) * (width + 2); l++) {
  1232.             name_layer[l] = '\0';
  1233.         }
  1234.  
  1235.         l = 0;
  1236.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1237.     
  1238.             nx = min(width, area.width - xmin);
  1239.             xmin1 = xmin - (y + 1) / 2 + x0;
  1240.             for (xx = xmin; xx < xmin + nx; xx++) {
  1241.             x = xx - (y + 1) / 2 + x0;
  1242.  
  1243.             /* we don't need the string here, so can overwrite */
  1244.             name = name_of_seen_unit_at(wrapx(x), y, side);
  1245.             summary = summary_of_seen_units_at(wrapx(x), y, side);
  1246.  
  1247.             if (!name && !summary)
  1248.               continue;
  1249.             l++;
  1250.             if (l > named_unit_max) {
  1251.                 named_unit_max *= 2;
  1252.                 name_position = (char *)
  1253.                   realloc(name_position,
  1254.                       (named_unit_max + 2) * sizeof(char));
  1255.             }
  1256.             name_position[l] = '\177';
  1257.  
  1258.             /* choose the free nearby hex with less named units around */
  1259.             nvalue = 99;
  1260.             for (l1 = 0; l1 < NUMDIRS; l1++) {
  1261.                 d = name_dir[l1];
  1262.                 point_in_dir(x, y, d, &x1, &y1);
  1263.                 if ((unit_seen_at(side, wrapx(x1), y1)
  1264.                  && on_page(x1 - xmin1, y1 - ymin))
  1265.                 || (name_at(x1 - xmin1, y1 - ymin) & N_BK))
  1266.                   continue;
  1267.                 mvalue = 0;
  1268.                 for_all_directions(d1) {
  1269.                 point_in_dir(x1, y1, d1, &x2, &y2);
  1270.                 if ((name_of_seen_unit_at(wrapx(x2), y2, side)
  1271.                      || summary_of_seen_units_at(wrapx(x2),
  1272.                                  y2, side))
  1273.                     && on_page(x2 - xmin1, y2 - ymin))
  1274.                   mvalue++;
  1275.                 }
  1276.                 if (mvalue<nvalue) {
  1277.                 name_position[l] = d;
  1278.                 nvalue = mvalue;
  1279.                 }
  1280.             }
  1281.             
  1282.             d = name_position[l];
  1283.             if (d < NUMDIRS && d >= 0) {
  1284.                 point_in_dir(x, y, d, &x1, &y1);
  1285.                 name_at(x1 - xmin1, y1 - ymin) |= N_BK;
  1286.                 /* comment containing debugging info */
  1287.                 fprintf(fp, "%% found pos for %s in (%d,%d) at (%d,%d), %s\n",
  1288.                     name_of_seen_unit_at(wrapx(x), y, side), x, y,
  1289.                     x1, y1, dirnames[d]);
  1290.             }
  1291.             }
  1292.         }
  1293.         
  1294.         /* print unit names.  (cx,cy) is the upper-left corner
  1295.            of the rectangle containing the hex */
  1296.  
  1297.         fprintf(fp, "gsave\n");
  1298.         fprintf(fp, "/%s findfont 8 scalefont setfont\n", ps_name[1]);
  1299.  
  1300.         l = 0;
  1301.         cy = hex_c;
  1302.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1303.             cy -= hex_c;
  1304.             
  1305.             cx = ((y % 2) == 0) * hex_w / 2 - hex_w;
  1306.             nx = min(width, area.width-xmin);
  1307.             xmin1 = xmin - (y + 1) / 2 + x0;
  1308.             for (xx = xmin; xx < xmin + nx; xx++) {
  1309.             cx += hex_w;
  1310.             x = xx - (y + 1) / 2 + x0;
  1311.  
  1312.             name = name_of_seen_unit_at(wrapx(x), y, side);
  1313.             /* free buffer */
  1314.             if (name) {
  1315.                 strcpy(tmpbuf,name);
  1316.                 name = tmpbuf;
  1317.             }
  1318.             summary = summary_of_seen_units_at(wrapx(x), y, side);
  1319.  
  1320.             if (!name && !summary)
  1321.               continue;
  1322.             l++;
  1323.  
  1324.             if (name_position[l] == '\177')
  1325.               continue;
  1326.             /* Good position found, otherwise delay until
  1327.                second round. */
  1328.             d = name_position[l];
  1329.             if (d < 0 || d >= NUMDIRS) {
  1330.                 name_position[l] = '\177';
  1331.                 continue;
  1332.             }
  1333.             point_in_dir(x, y, d, &x1, &y1);
  1334.             /* comment containing debugging info */
  1335.             fprintf(fp, 
  1336.                 "%% calling print_unit_legends(fp, \"%s\", \"%s\", \\x%2.2x, %s)\n",
  1337.                 name ? name : "", summary ? summary : "",
  1338.                 name_at(x1-xmin1, y1-ymin),
  1339.                 dirnames[(int) name_position[l]]);
  1340.             if (!print_unit_legends(fp, name, summary,
  1341.                         &(name_at(x1 - xmin1, y1 - ymin)),
  1342.                         (int) name_position[l], cx, cy))
  1343.               name_position[l] = '\127';
  1344.             }
  1345.         }
  1346.         }
  1347.         fprintf(fp, "grestore\n");
  1348.  
  1349.         /* print remaining unit names.  (cx,cy) is the upper-left corner
  1350.            of the rectangle containing the hex */
  1351.  
  1352.         fprintf(fp, "gsave\n");
  1353.  
  1354.         l = 0;
  1355.         cy = hex_c;
  1356.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1357.         cy -= hex_c;
  1358.         
  1359.         cx = ((y % 2) == 0) * hex_w / 2 - hex_w;
  1360.         nx = min(width, area.width-xmin);
  1361.         for (xx = xmin; xx < xmin + nx; xx++) {
  1362.             cx += hex_w;
  1363.             x = xx - (y + 1) / 2 + x0;
  1364.             x = wrapx(x);
  1365.             
  1366.             name = name_of_seen_unit_at(x, y, side);
  1367.             /* free buffer */
  1368.             if (name) {
  1369.             strcpy(tmpbuf,name);
  1370.             name = tmpbuf;
  1371.             }
  1372.             summary = summary_of_seen_units_at(x, y, side);
  1373.             if (!name)
  1374.               name = summary;
  1375.             if (!name)
  1376.               continue;
  1377.             l++;
  1378.  
  1379.             if ((name_layer
  1380.              && name_position
  1381.              && name_position[l] == '\177')
  1382.             || !name_layer
  1383.             || !name_position) {
  1384.             /* choose font */
  1385.             if (8 * ps_string_width(name, 1) > 1000 * hex_w) {
  1386.                 font = 0;
  1387.             } else {
  1388.                 font = 1;
  1389.             }
  1390.             fprintf(fp, "/%s findfont 8 scalefont setfont\n",
  1391.                 ps_name[font]);
  1392.             
  1393.             fprintf(fp, "gsave %d %g translate (%s) n grestore\n",
  1394.                 cx, cy/asym, name);
  1395.             }
  1396.         }
  1397.         }
  1398.         fprintf(fp, "grestore\n");
  1399.  
  1400.         /* show units & side emblems.  (cx,cy) is the upper-left corner
  1401.            of the rectangle containing the hex */
  1402.  
  1403.         fprintf(fp, "gsave\n");
  1404.  
  1405.         cy = hex_c;
  1406.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1407.         cy -= hex_c;
  1408.     
  1409.         cx = ((y % 2) == 0) * hex_w / 2 - hex_w;
  1410.         nx = min(width, area.width - xmin);
  1411.         for (xx = xmin; xx < xmin + nx; xx++) {
  1412.             cx += hex_w;
  1413.             x = xx - (y + 1) / 2 + x0;
  1414.             x = wrapx(x);
  1415.             if (!in_area(x, y))
  1416.               continue;
  1417.     
  1418.             if ((utype = utype_seen_at(side, x, y)) != NONUTYPE) {
  1419.             fprintf(fp, "gsave %d %g translate u%X s%X grestore\n",
  1420.                 cx, cy / asym, utype,
  1421.                 sideno_of_seen_unit_at(x, y, side));
  1422.             }
  1423.         }
  1424.         }
  1425.         fprintf(fp, "grestore\n");
  1426.  
  1427.         fprintf(fp, "showpage\n");
  1428.     }
  1429.     }
  1430.     /* End of loop over pages. */
  1431.     
  1432.     fprintf(fp, "%%%%Trailer\n");
  1433.     fprintf(fp, "%%%%Pages: %d\n", page);
  1434.     fprintf(fp, "%%%%EOF\n");
  1435.     
  1436.     fclose(fp);
  1437.     if (legs)
  1438.       free(legs);
  1439.     if (name_layer)
  1440.       free(name_layer);
  1441.     if (name_position)
  1442.       free(name_position);
  1443. }
  1444.  
  1445. /* Compute the width of a PostScript string, in thousands of the
  1446.    "nominal size". */
  1447.  
  1448. static int 
  1449. ps_string_width(str, font)
  1450. char *str;
  1451. int font;
  1452. {
  1453.     int ch, i = 0, esc = 0;
  1454.     
  1455.     if (!str)
  1456.       return 0;
  1457.     
  1458.     for (; *str; str++) {
  1459.     ch = *str;
  1460.     if (ch == '\\' && !esc) {
  1461.         esc = 1;
  1462.     } else {
  1463.         if (!between(0, ch, 127)) {
  1464.         run_warning("disallowed char %d in PS string", ch);
  1465.         ch = 'X';
  1466.         }
  1467.         i += ps_width[font][ch];
  1468.         esc = 0;
  1469.     }
  1470.     }
  1471.     return i;
  1472. }
  1473.  
  1474. static int
  1475. print_unit_legends(fp, name, summary, m, dir, cx, cy)
  1476. FILE *fp;
  1477. char *name, *summary, *m;
  1478. int dir, cx, cy;
  1479. {
  1480.     int left, wide_name, wide_summ, down;
  1481.     
  1482.     if (dir < 0 || dir >= NUMDIRS)
  1483.       return 0;
  1484.     
  1485.     cx += hex_w * dirx[dir] + hex_w / 2 * diry[dir];  cy += hex_c * diry[dir];
  1486.     if (dir == EAST)
  1487.       cx -= 4;
  1488.     if (dir == WEST)
  1489.       cx += 4;
  1490.     if (dir == NORTHEAST || dir == NORTHWEST)
  1491.       cy -= 1;
  1492.     if (dir == SOUTHEAST || dir == SOUTHWEST)
  1493.       cy += 4;
  1494.     
  1495.     left = (dir == EAST || dir == NORTHEAST || dir == SOUTHEAST);
  1496.     wide_name = (8 * ps_string_width(name, 1) > 1000 * hex_w);
  1497.     wide_summ = (8 * ps_string_width(summary, 1) > 1000 * hex_w);
  1498.     
  1499.     if (name) {
  1500.     if (summary && !(*m & N_U)) {
  1501.         down = 0;
  1502.     } else if (!(*m & N_U) && !(*m & N_D)) {
  1503.         down = (dir != SOUTHEAST && dir != SOUTHWEST);
  1504.     } else if (!(*m & N_D)) {
  1505.         down = 1;
  1506.     } else if (!(*m & N_U)) {
  1507.         down = 0;
  1508.     } else {
  1509.         return 0;
  1510.     }
  1511.     if (!down && !(*m & N_D) && summary) {
  1512.         if (dir == NORTHEAST || dir == NORTHWEST) {
  1513.         cy -= 3;
  1514.         } else if (dir == EAST || dir == WEST) {
  1515.         cy -= 2;
  1516.         }
  1517.     }
  1518.  
  1519.     fprintf(fp, "gsave %d %g translate (%s) n%c%c grestore\n",
  1520.         cx, cy/asym, name, left ? 'l' : 'r', down ? 'd' : 'u');
  1521.     *m |= down ? N_D : N_U;
  1522.     }
  1523.     
  1524.     if (summary) {
  1525.     if (name)
  1526.       cy += 3;
  1527.     if (!(*m & N_U) && !(*m & N_D)) {
  1528.         down = (dir != SOUTHEAST && dir != SOUTHWEST);
  1529.     } else if (!(*m & N_D)) {
  1530.         down = 1;
  1531.     } else if (!(*m & N_U)) {
  1532.         down = 0;
  1533.     } else {
  1534.         return 1;
  1535.     }
  1536.  
  1537.     fprintf(fp, "gsave %d %g translate (%s) n%c%c grestore\n",
  1538.         cx, cy/asym, summary, left ? 'l' : 'r', down ? 'd' : 'u');
  1539.     *m |= down ? N_D : N_U;
  1540.     }
  1541.     return 1;
  1542. }
  1543.