/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2010 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: December 2010 */


/* This file contains code for outputting things in PostScript */


#include "pmwhdr.h"
#include "outhdr.h"




/************************************************
*             Static variables                  *
************************************************/

static BOOL ps_EPS = FALSE;
static BOOL ps_slurA = FALSE;
static int  ps_caj = 0;
static int  ps_chcount;
static int  ps_curfont;
static int  ps_curfontsize;
static BOOL ps_curfontX;
static int  ps_gray;
static int  ps_sheetwidth;
static int  ps_xmargin;
static int  ps_ymax;

static uschar *ps_IdStrings[font_tablen+1];
static int  ps_curfonttransform[6];



/************************************************
*                 Macros                        *
************************************************/

/* Coordinate translation for character output */

#define psxtran(x) ((x) + ps_xmargin)

#define psytran(y) (ps_ymax - (y))




/*************************************************
*           Write to PS file, wrapping           *
*************************************************/

/* To keep the PostScript readable we wrap it mostly at 72 characters wide.

Arguments:
  format      a format string
  ...         arguments for the format
  
Returns:      nothing 
*/

static 
void ps_printf(char *format, ...)
{
int len;
uschar buff[256];
uschar *p = buff;
va_list ap;

va_start(ap, format);
format_vsprintf(buff, format, ap);
len = Ustrlen(buff);
va_end(ap);

if (ps_chcount > 0 && ps_chcount + len > 72)
  {
  fputc('\n', ps_file);
  ps_chcount = 0;
  }

if (ps_chcount == 0 && *p == ' ') { p++; len--; }
Ufputs(p, ps_file);
if (p[len-1] == '\n') ps_chcount = 0;
  else ps_chcount += len;
}



/*************************************************
*         Check whether font needs changing      *
*************************************************/

/* The X argument is used when the character is > 255, indicating that we want
the second version of the font, with the alternative encoding.

Arguments:
  f              font number
  s              the font size
  X              TRUE if it's the extended font we want

Returns:         TRUE if the font needs changing
*/

static BOOL
ps_needchangefont(int f, int s, BOOL X)
{
int i;
if (f != ps_curfont || X != ps_curfontX || s != ps_curfontsize) return TRUE;
for (i = 0; i <= 5; i++)
  if (font_transform[i] != ps_curfonttransform[i]) return TRUE;
return FALSE;
}


/*************************************************
*           Make a given font current            *
*************************************************/

/* This function is called when we know that a font change is needed.

Arguments:
  f              current font number
  s              the font size
  X              TRUE if it's the extended font we want

Returns:         nothing
*/

static void 
ps_setfont(int f, int s, BOOL X)
{
ps_curfont = f;
ps_curfontX = X;
ps_curfontsize = s;
memcpy(ps_curfonttransform, font_transform, 6*sizeof(int));

/* Transformation is null: don't waste time/space by writing it */

if (font_transform[0] == 65536 &&
    font_transform[1] == 0 &&
    font_transform[2] == 0 &&
    font_transform[3] == 65536 &&
    font_transform[4] == 0 &&
    font_transform[5] == 0)
  ps_printf(" %s%s %f ss%s", ps_IdStrings[f], X? "X" : "", s,
    main_righttoleft? "r" : "");

/* A genuine transform is set */

else ps_printf(" %s%s [%f %f %f %f %f %f] sm%s",
  ps_IdStrings[f],
  X? "X" : "",
  mac_muldiv(font_transform[0], s, 65536),
  mac_muldiv(font_transform[1], s, 65536),
  mac_muldiv(font_transform[2], s, 65536),
  mac_muldiv(font_transform[3], s, 65536),
  mac_muldiv(font_transform[4], s, 1000),
  mac_muldiv(font_transform[5], s, 1000),
  main_righttoleft? "r" : "");
}




/*************************************************
*             End a text string                  *
*************************************************/

/* This function writes the terminating ')' and an appropriate command.

Arguments:
  absolute      TRUE if the coordinates are absolute, else relative
  w             extra space width
  x             x-coordinate
  y             y-coordinate
*/

static void
ps_endstring(BOOL absolute, int w, int x, int y)
{
fputc(')', ps_file);   /* Does not check ps_chcount */
ps_chcount++;

if (absolute)
  {
  if (w != 0) ps_printf("%f %f %f ws", w, psxtran(x), psytran(y));
    else ps_printf("%f %f s", psxtran(x), psytran(y));
  }
else if (x == 0 && y == 0)  /* Relative, but no movement */
  {
  if (w != 0) ps_printf("%f wsh", w); else ps_printf("sh");
  }
else
  {
  if (w != 0) ps_printf("%f %f %f wrs", w, x, y);
    else ps_printf("%f %f rs", x, y);
  }
}



/*************************************************
*               Basic string output code         *
*************************************************/

/* This function scans a string's characters and converts to the appropriate 
output encoding for the PostScript fonts.

Arguments:
  s            the string, in UTF-8 format
  f            the font
  pointsize    the font size
  absolute     TRUE if the coordinates are absolute, else relative 
  x            the x coordinate
  y            the y coordinate
  w            extra width for spaces (for justifying)
  
Returns:       nothing 
*/

static void
ps_basic_string(uschar *s, int f, int pointsize, BOOL absolute, int x, int y,
  int w)
{
int font = font_table[f];
kerntablestr *ktable;
fontstr *fs = &(font_List[font]);
BOOL instring = FALSE;
BOOL stdencoding = fs->stdencoding;
uschar *p = s;
uschar *endp = s + Ustrlen(s);

ktable = fs->kerns;

/* Check top/bottom of bbox for EPS. Allow 0.4*pointsize for descenders below
and pointsize above. */

if (ps_EPS)
  {
  int descender = (4 * pointsize)/10; 
  if (y + descender > out_bbox[1]) out_bbox[1] = y + descender;
  if (y - pointsize < out_bbox[3]) out_bbox[3] = y - pointsize;
  } 

/* When printing right-to-left, we need to find the complete length of the 
string so that we can print it from the other end, since the fonts still work
left-to-right. We also need the length for EPS output, in order to set the
bounding box. Finding the length requires a preliminary scan of the whole
thing, somewhat repeating the printing scan below, but it was too messy to try
to conflate the code. */

if (main_righttoleft || ps_EPS)
  {
  int swidth = 0;
  int last_width = 0;
  int last_r2ladjust = 0;
   
  for (;;)
    {
    int c; 
    
    GETCHARINC(c, p);
    if (c == 0) break; 
    
    if (c >= 256)
      {
      if (!stdencoding) 
        { 
        last_width = fs->widths[UNKNOWN_CHAR_N]; 
        last_r2ladjust = fs->r2ladjusts[UNKNOWN_CHAR_N]; 
        } 
    
      else if (c < LOWCHARLIMIT) 
        { 
        last_width = fs->widths[c]; 
        last_r2ladjust = fs->r2ladjusts[c]; 
        } 
         
      else
        {
        uschar utf[8];
        tree_node *t;
        utf[misc_ord2utf8(c, utf)] = 0;
        t = Tree_Search(fs->high_tree, utf);
        if (t == NULL)
          { 
          last_width = fs->widths[UNKNOWN_CHAR_S]; 
          last_r2ladjust = fs->r2ladjusts[UNKNOWN_CHAR_S]; 
          } 
        else 
          {
          last_width = t->val[1]; 
          last_r2ladjust = t->val[2]; 
          } 
        }
      }
      
    else
      {
      int cj = c; 
    
      /* Fudges for various special cases in the music font, where the 
      adjustment bounding box is taken from another character. */
    
      if (f == font_mf)
        { 
        switch(c)
          {
          case 'J':       /* Additional stem characters - adjust as for note */
          case 'K':
          case 'o':
          case 'p':
          case 'q':
          case 'r':      
          cj = '5';
          break;
          
          case '7':       /* Up quaver - adjust as for down quaver */
          cj = '8';
          break;
           
          case '9':       /* Up semiquaver - adjust as for down semiquaver */
          cj = ':';
          break;
          
          default:
          break;
          }        
        }
   
      last_width = fs->widths[c];
      last_r2ladjust = fs->r2ladjusts[cj];
      } 
      
    /* Amass the total string width */
    
    swidth += last_width;
     
    /* If there is another character, scan the kerning table */
  
    if (main_kerning && fs->kerncount > 0 && *p != 0)
      {
      int bot = 0;
      int top = fs->kerncount;
      int cc;
      unsigned int pair;
  
      GETCHAR(cc, p);
      pair = (c << 16) | cc;
  
      while (bot < top)
        {
        kerntablestr *k;
        int mid = (bot + top)/2;
        k = &(ktable[mid]);
  
        if (pair == k->pair)
          {
          swidth += k->kwidth;
          break;
          }
        if (pair < k->pair) top = mid; else bot = mid + 1;
        }
      }
    }  
    
  /* For right-to-left, adjust the printing position for the string by the
  length of the string, adjusted for the actual bounding box of the final
  character, and scaled to the font size. For EPS output, adjust the bounding 
  box. Both may, of course, happen. */

  if (main_righttoleft)
    x += mac_muldiv(swidth - last_width + last_r2ladjust, pointsize, 1000);
     
  if (ps_EPS)
    {
    swidth = mac_muldiv(swidth, pointsize, 1000);    
    if (x + swidth > out_bbox[2]) out_bbox[2] = x + swidth;
    } 
  } 

/* Scan the string and generate the output. */

p = s;
for (;;)
  {
  int c, pc; 
  BOOL extended = FALSE;

  GETCHARINC(c, p);
  if (c == 0) break;
  
  pc = c;    /* The character value to print */ 
  
  /* For standardly encoded fonts, code points >= 256 and < LOWCHARLIMIT are
  encoded in the second of the two PostScript fonts, using the Unicode encoding
  less 256. The remaining code points have to be converted to some of the
  remaining characters in the PostScript font, which are encoded arbitrarily,
  i.e. not using the Unicode encoding (some of their Unicode values are quite 
  large). To find this encoding, we search for the character in the widths tree
  for the font, where the offset is also stored. */
  
  if (c >= 256)
    {
    if (!stdencoding) 
      { 
      pc = UNKNOWN_CHAR_N;  /* Known to be < 256 */
      error_moan(78, c, UNKNOWN_CHAR_N); 
      } 

    else if (c < LOWCHARLIMIT) 
      { 
      pc = c - 256; 
      extended = TRUE; 
      } 
       
    else
      {
      uschar utf[8];
      tree_node *t;
      utf[misc_ord2utf8(c, utf)] = 0;
      t = Tree_Search(fs->high_tree, utf);
      if (t == NULL)
        { 
        pc = UNKNOWN_CHAR_S;
        error_moan(79, c, UNKNOWN_CHAR_S); 
        } 
      else 
        {
        pc = LOWCHARLIMIT + t->val[0] - 256;
        extended = TRUE;
        } 
      }
    }
    
  /* Change between base and extended font if necessary */

  if (ps_needchangefont(f, pointsize, extended))
    {
    if (instring)
      {
      ps_endstring(absolute, w, x, y);
      x = y = 0; 
      absolute = instring = FALSE;
      }
    ps_setfont(f, pointsize, extended);
    }
    
  /* Arrange to print the byte */

  if (!instring)
    {
    if (ps_chcount > 0 && ps_chcount + endp - p > 72)
      {
      fputc('\n', ps_file);
      ps_chcount = 0;
      }
    fputc('(', ps_file);
    ps_chcount++; 
    instring = TRUE;
    }

  if (pc == '(' || pc == ')' || pc == '\\') 
    ps_chcount += fprintf(ps_file, "\\%c", pc);
  else if (pc >= 32 && pc <= 126) 
    {
    fputc(c, ps_file);
    ps_chcount++;
    }  
  else ps_chcount += fprintf(ps_file, "\\%03o", pc);

  /* If there is another character, scan the kerning table */

  if (main_kerning && fs->kerncount > 0 && *p != 0)
    {
    int xadjust = 0, yadjust = 0;
    int bot = 0;
    int top = fs->kerncount;
    int cc;
    unsigned int pair;

    GETCHAR(cc, p);
    pair = (c << 16) | cc;

    while (bot < top)
      {
      kerntablestr *k;
      int mid = (bot + top)/2;
      k = &(ktable[mid]);

      if (pair == k->pair)
        {
        xadjust = k->kwidth;
        break;
        }
      if (pair < k->pair) top = mid; else bot = mid + 1;
      }

    /* If a kern was found, scale the adjustment to the font size, and for the
    string rotation and transformation, if any. Then close the previous
    substring and arrange that the next be output relative if this is the
    first. */

    if (xadjust != 0)
      {
      xadjust = mac_muldiv(xadjust, pointsize, 1000);
      yadjust = mac_muldiv(xadjust, font_transform[1], 65536);
      xadjust = mac_muldiv(xadjust, font_transform[0], 65536);
      ps_endstring(absolute, w, x, y);
      absolute = FALSE;
      instring = FALSE; 
      x = main_righttoleft? -xadjust : xadjust;
      y = yadjust;
      }
    }
  }

if (instring) ps_endstring(absolute, w, x, y);
}




/*************************************************
*      Output a string with space stretching     *
*************************************************/

/*
Arguments:
  s          the string
  font       the font
  pointsize  the pointsize for the font
  x          the absolute x coordinate
  y          the absolute y coordinate
  w          the extra space for each space
  
Returns:     nothing
*/      

void 
ps_wtext(uschar *s, int font, int pointsize, int x, int y, int w)
{
ps_basic_string(s, font, pointsize, TRUE, x, y, w);
}



/*************************************************
*          Output string in music font           *
*************************************************/

/* There are two versions, one with absolute coordinates, and one with relative 
coordinates, to save having to pass a flag each time (most of the calls are 
with absolute coordinates).

Arguments:
  s          the string
  pointsize  the pointsize for the font
  x          the absolute x coordinate
  y          the absolute y coordinate
  
Returns:     nothing
*/      

void 
ps_musstring(uschar *s, int pointsize, int x, int y)
{
ps_basic_string(s, font_mf, pointsize, TRUE, x, y, 0);
}

void 
ps_relmusstring(uschar *s, int pointsize, int x, int y)
{
ps_basic_string(s, font_mf, pointsize, FALSE, x, y, 0);
}





/*************************************************
*     Output a text string and change origin     *
*************************************************/

/* The x and y coordinates are updated if requested - note that y goes
downwards.

Arguments:
  s          the string
  font       the font
  pointsize  the pointsize for the font
  x          pointer to the absolute x coordinate
  y          pointer to the absolute y coordinate
  update     if TRUE, update the x,y positions
  
Returns:     nothing
*/      

void
ps_string(uschar *s, int f, int pointsize, int *x, int *y, BOOL update)
{
BOOL skip = FALSE;
int truepointsize = (f == font_mu)? (pointsize*9)/10 : pointsize;

/* Output the string, unless it is a music font string consisting only of
printing point moving characters. */

if (f == font_mu || f == font_mf)
  {
  uschar *ss = s;
  skip = TRUE;
  while (*ss)
    {
    int ch;
    GETCHARINC(ch, ss);
    if (ch < 118 || (ch > 126 && ch < 185) || ch > 188) 
      { skip = FALSE; /* break; */ } 
    }
  }

if (!skip) ps_basic_string(s, f, truepointsize, TRUE, *x, *y, font_xstretch);

/* Now arrange to return the new current point if required */

if (update)
  {
  *x += font_stringwidth(s, f, pointsize);
  *y -= font_stringheight;
  }
}





/*************************************************
*             Output a bar line                  *
*************************************************/

/*
Arguments:
  x          the x coordinate
  ytop       the top of the barline
  ybot       the bottom of the barline
  type       the type of barline
  
Returns:     nothing
*/    

void 
ps_barline(int x, int ytop, int ybot, int type)
{
int magn = (curmovt->barlinesize > 0)? curmovt->barlinesize : main_stavemagn;

/* Solid barlines and dashed ones of a single stave's depth are
done with characters from the music font, except when the barline size is
greater than the stave magnification and it's just one stave deep. */

if ((type != bar_dotted || ytop == ybot) && 
    (magn <= main_stavemagn || ytop != ybot))
  {
  if (main_righttoleft)
    x += mac_muldiv(font_List[font_mu].r2ladjusts[type], 10*magn, 1000);
  if (ps_needchangefont(font_mf, 10*magn, FALSE))
    ps_setfont(font_mf, 10*magn, FALSE);
  if (magn != main_stavemagn) ytop += 16*(magn - main_stavemagn);
  if (ytop != ybot)
    ps_printf(" %f %f(%c)%f %f b", 16*magn,
      psytran(ybot), type, psxtran(x), psytran(ytop));
  else ps_printf("(%c)%f %f s", type, psxtran(x), psytran(ytop));
  }

/* Long dashed lines have to be drawn, as do other lines if they are shorter
than the character - this happens if barlinesize is greater than the stave
magnification. */

else
  {
  int half_thickness = (type == bar_thick)? magn : magn/5;
  int yadjust = main_stavemagn/5;
  x += half_thickness;

  if (type == bar_dotted)
    ps_printf(" %f %f %f %f %f [%f %f] dl", psxtran(x),
      psytran(ytop - 16*main_stavemagn - yadjust),
        psxtran(x), psytran(ybot - yadjust), 2*half_thickness, 7*half_thickness,
          7*half_thickness);

  else
    {
    ps_printf(" %f %f %f %f %f l", psxtran(x),
      psytran(ytop - 16*main_stavemagn - yadjust),
        psxtran(x), psytran(ybot - yadjust), 2*half_thickness);
    if (type == bar_double)
      {
      int xx = x + 2*magn;
      ps_printf(" %f %f %f %f %f l", psxtran(xx),
        psytran(ytop - 16*main_stavemagn - yadjust),
          psxtran(xx), psytran(ybot - yadjust), 2*half_thickness);
      }
    }
  }
}




/*************************************************
*             Output a brace                     *
*************************************************/

/*
Arguments:
  x          the x coordinate
  ytop       the y coordinate of the top of the brace
  ybot       the y coordinate of the bottom of the brace
  magn       the magnification
  
Returns:     nothing
*/   

void 
ps_brace(int x, int ytop, int ybot, int magn)
{
ps_printf(" %f %f %f br%s", ((ybot-ytop+16*magn)*23)/12000, psxtran(x)+1500,
  psytran((ytop-16*magn+ybot)/2), (curmovt->bracestyle)? "2":"");
}




/*************************************************
*             Output a bracket                   *
*************************************************/

/*
Arguments:
  x          the x coordinate
  ytop       the y coordinate of the top of the bracket
  ybot       the y coordinate of the bottom of the bracket
  magn       the magnification
  
Returns:     nothing
*/   

void 
ps_bracket(int x, int ytop, int ybot, int magn)
{
ps_printf(" %f %f %f k", psxtran(x), psytran(ytop)+16*magn, psytran(ybot));
}



/*************************************************
*            Output a stave's lines              *
*************************************************/

/* Assume staves are always at least one character long. The stavelines
parameter will always be > 0. It has been reported that some PostScript
interpreters can't handle the 100-point wide characters, so there is an option
to use only the 10-point characters.

Arguments:
  leftx        the x-coordinate of the stave start
  y            the y-coordinate of the stave start
  rightx       the x-coordinate of the stave end
  stavelines   the number of stave lines

Returns:       nothing
*/

void
ps_stave(int leftx, int y, int rightx, int stavelines)
{
uschar sbuff[8];
uschar buff[256];
int ch, i;
int chwidth = 0;
int x = leftx;

if (stave_use_widechars)
  {
  ch = out_stavechar10[stavelines];
  i = 100;
  }
else
  {
  ch = out_stavechar1[stavelines];
  i = 10;
  }

/* Select appropriate size of music font */

if (ps_needchangefont(font_mf, 10*main_stavemagn, FALSE))
  ps_setfont(font_mf, 10*main_stavemagn, FALSE);

/* Build character string of (optionally) 100-point & 10-point chars; some of
them are non-printing and have to be octal-escaped. */

Ustrcpy(buff, "(");
for (; i >= 10; i /= 10)
  {
  if (ch < 127) { sbuff[0] = ch; sbuff[1] = 0; }
    else sprintf(CS sbuff, "\\%03o", ch);
  chwidth = i*main_stavemagn;
  while (rightx - x >= chwidth) { Ustrcat(buff, sbuff); x += chwidth; }
  ch = out_stavechar1[stavelines];
  }

/* Now print it, forcing it onto a separate line (for human legibility). We use
BIGNUMBER/2 because the routine adds the length to ps_chcount to check for
overflow. */

Ustrcat(buff, ")");
if (ps_chcount) ps_chcount = BIGNUMBER/2;
ps_printf("%s%f %f s", buff, psxtran(main_righttoleft? x:leftx), psytran(y));

/* If there's a fraction of 10 points left, deal with it */

if (x < rightx)
  ps_printf(" (%s)%f %f s", sbuff, 
    psxtran(main_righttoleft? rightx : (rightx - chwidth)), psytran(y));

ps_printf("\n");
ps_chcount = 0;
}





/*************************************************
*           Output one musical character         *
*************************************************/

/* Certain musical characters are given identity numbers that may or may not
correspond directly to characters in the music font. The table called
out_mftable[] defines how they are to be printed.

Arguments:
  x          the x coordinate
  y          the y coordinate
  ch         the character's identity number
  pointsize  the point size
  
Returns:     nothing    
*/

void 
ps_muschar(int x, int y, int ch, int pointsize)
{
int xfudge = 0;
mfstr *p = out_mftable_ps[ch];
uschar s[10];

/* One-to-one mappings are represented by small integers */

if ((long int)p < 256)
  {
  s[misc_ord2utf8((long int)p, s)] = 0;
  ps_basic_string(s, font_mf, pointsize, TRUE, x, y, 0);
  }

/* Otherwise there may be a chain of strings/displacements */

else do
  {
  int c = p->ch;
  int i = 0;
  int nxfudge = 0;  
  
  /* Nasty fudge for bracketed accidentals in right-to-left mode: when the
  brackets come as individual values, swap them round and fudge the spacing of 
  the remaining chars. This is needed for flats, in practice. */
  
  if (main_righttoleft) switch (c)
    {
    case 139: c = 140; nxfudge = -1600; break;
    case 140: c = 139; break;  
    case 141: c = 142; nxfudge = -1600; break;
    case 142: c = 141; break;
    default: break;  
    }   

  /* Extract up to 4 music font characters from c */
   
  while (c)
    {
    i += misc_ord2utf8(c & 255, s + i);
    c >>= 8;
    }
  s[i] = 0;

  ps_basic_string(s, font_mf, pointsize, TRUE,
    x + mac_muldiv(p->x, pointsize, 10000) + xfudge,
    y - mac_muldiv(p->y, pointsize, 10000), 0);

  p = p->next;
  xfudge += nxfudge; 
  }
while (p != NULL);
}



/*************************************************
*            Output a beam line                  *
*************************************************/

/* This function is called several times for a multi-line beam, with the level 
number increasing each time. Information about the slope and other features is
in beam_* variables.

Arguments:
  x0            starting x coordinate, relative to start of bar
  x1            ending x coordinate, relative to start of bar
  level         level number 
  levelchange   set nonzero for accellerando and ritardando beams
  
Returns:        nothing
*/   

void 
ps_beam(int x0, int x1, int level, int levelchange)
{
int y0, y1;
int sign = (beam_upflag)? (+1) : (-1);
int depth = -main_stavemagn*((n_fontsize * sign *
  (int)(((double)curmovt->beamdepth) /
    cos(atan((double)beam_slope/1000.0))))/10000)/1000;

y1 = y0 = out_ystave - beam_firstY +
  mac_muldiv(n_fontsize, (level - 1) * sign * 3 * main_stavemagn, 10000);

y0 -= mac_muldiv(x0-beam_firstX, beam_slope, 1000);
y1 -= mac_muldiv(x1-beam_firstX, beam_slope, 1000);

/* For accellerando and ritardando beams, adjust the ends, and make a little
bit thinner. */

if (levelchange != 0)
  {
  int adjust = mac_muldiv(n_fontsize,
    abs(levelchange) * sign * 4 * main_stavemagn, 10000);
  depth = (depth*17)/20;
  if (levelchange < 0)
    {
    y0 += adjust;
    y1 += adjust/8;
    }
  else
    {
    y0 += adjust/8;
    y1 += adjust;
    }
  }

/* Get absolute x values and write the PostScript */

x0 += out_barx;
x1 += out_barx;

/* When printing right-to-left, adjust by one note's printing adjustment. 
The value can't just be read from the font, as it needs fiddling, so we
just fudge a fixed value. */

if (main_righttoleft)
  {
  int adjust = sign * mac_muldiv(n_fontsize/2, main_stavemagn, 1000); 
  x0 -= adjust;
  x1 -= adjust;
  } 

ps_printf(" %f %f %f %f %f m",
  depth, psxtran(x1), psytran(y1), psxtran(x0), psytran(y0));
}



/*************************************************
*            Output a slur                       *
*************************************************/

/* This was the original way of drawing slurs. Additional complication in slurs
has resulted in a function called out_slur() that uses more primitive output
functions, and which could in principle be used for all slurs. However, we
retain ps_slur for complete, non-dashed, curved slurs for compatibility and to
keep the size of the PostScript down. 

Arguments:
  x0         start x coordinate
  y0         start y coordinate
  x1         end x coordinate
  y1         end y coordinate   
  flags      slur flags
  co         "centre out" adjustment
  
Returns:     nothing   
*/

void 
ps_slur(int x0, int y0, int x1, int y1, int flags, int co)
{
int length = x1 - x0;

y0 = out_ystave - y0;
y1 = out_ystave - y1;

x0 += 3*main_stavemagn;
x1 += 3*main_stavemagn;

co = ((co + ((length > 20000)? 6000 : (length*6)/20)) * main_stavemagn)/1000;

if ((out_slurclx | out_slurcly | out_slurcrx | out_slurcry) != 0)
  {
  ps_printf(" %f %f %f %f cA", out_slurclx, out_slurcly, out_slurcrx, out_slurcry);
  ps_slurA = TRUE;
  }
else if (ps_slurA)
  {
  ps_printf(" 0 0 0 0 cA");     /* default extra control movements */
  ps_slurA = FALSE;
  }

/* Keeping these as two separate calls enables the output to be split; changing
would require test output to be reset. */

ps_printf(" %f %f %f %f", psxtran(x0), psytran(y0), psxtran(x1), psytran(y1));
ps_printf(" %f cv%s%s",
  ((flags & sflag_b) != 0)? (-co) : co,
  ((flags & sflag_w) == 0)? "" : "w",
  ((flags & sflag_e) == 0)? "" : "e");
}




/*************************************************
*            Output a straight line              *
*************************************************/

/*
Arguments:
  x0          start x coordinate
  y0          start y coordinate
  x1          end x coordinate
  y1          end y coordinate
  thickness   line thickness
  flags       for various kinds of line
  
Returns:      nothing
*/      

void 
ps_line(int x0, int y0, int x1, int y1, int thickness, int flags)
{
uschar *reset = US"";
double xx = (double)((int)(x1 - x0));
double yy = (double)((int)(y1 - y0));
int len =  (int)(sqrt(xx*xx + yy*yy));
int dashlength = 0;
int gaplength = 0;
int dashcount, spacecount;

/* Handle "editorial" lines: won't exist if dashed or dotted */

if ((flags & tief_editorial) != 0)
  {
  ps_printf(" GS %f %f T %f R 0 2.0 Mt 0 -2.0 Lt S GR",
   psxtran((x0+x1)/2), psytran(out_ystave - (y0+y1)/2),
   (int)(atan2(yy, xx)*180000.0/3.14159));
  }

/* Compute new dash parameters if required */

if ((flags & tief_dashed) != 0)
  {
  dashlength = 3*main_stavemagn;
  dashcount = (len/dashlength) | 1;
  spacecount = dashcount/2;
  if (dashcount != 1)
    {
    gaplength = (len - ((dashcount+1)*dashlength)/2)/spacecount;
    ps_printf("[%f %f] 0 Sd", dashlength, gaplength);
    reset = US"[] 0 Sd";
    }
  }
else if ((flags & tief_dotted) != 0)
  {
  dashlength = 100;
  dashcount = (len + 4*main_stavemagn)/(4*main_stavemagn + dashlength);
  if (dashcount > 1)
    {
    gaplength = (len - dashcount * dashlength)/(dashcount - 1);
    ps_printf(" 1 Slc[%f %f] 0 Sd", dashlength, gaplength);
    thickness = main_stavemagn;
    reset = US" 0 Slc[] 0 Sd";
    }
  }

/* If just set dash parameters, take note of the save flag. */

if (gaplength > 0)
  {
  if ((flags & tief_savedash) != 0)
    {
    reset = US"";
    out_dashlength = dashlength;
    out_dashgaplength = gaplength;
    }
  else out_dashlength = out_dashgaplength = 0;
  }

/* Do the line */

ps_printf(" %f %f %f %f %f l%s", psxtran(x1), psytran(out_ystave - y1),
  psxtran(x0), psytran(out_ystave - y0), thickness, reset);
}




/*************************************************
*         Output a series of lines               *
*************************************************/

/* This is only used for sequences of plain lines (no dashes, etc.)

Arguments:
  x           vector of x coordinates
  y           vector of y coordinates
  count       number of vector elements
  thickness   line thickness
  
Returns:      nothing
*/    

void 
ps_lines(int *x, int *y, int count, int thickness)
{
int i;
for (i = count - 1; i > 0; i--)
  ps_printf(" %f %f", psxtran(x[i]), psytran(out_ystave - y[i]));
ps_printf(" %d %f %f %f ll", count - 1, psxtran(x[0]),
  psytran(out_ystave - y[0]), thickness);
}




/*************************************************
*         Output and stroke or fill a path       *
*************************************************/

/* The path can contain moves, lines, and curves. We generate in-line
PostScript for this one, using the saved grey level.

Arguments:
  x            vector of x coordinates
  y            vector of y coordinates
  c            vector of move/line/curve operators
  thickness    thickness of the lines for stroke; negative for fill
  
Returns:       nothing    
*/

void 
ps_path(int *x, int *y, int *c, int thickness)
{
while (*c) switch(*c++)
  {
  case path_move:
  ps_printf(" %f %f Mt", psxtran(*x++), psytran(out_ystave - *y++));
  break;

  case path_line:
  ps_printf(" %f %f Lt", psxtran(*x++), psytran(out_ystave - *y++));
  break;

  case path_curve:
  ps_printf(" %f %f %f %f %f %f Ct",
    psxtran(x[0]), psytran(out_ystave - y[0]),
      psxtran(x[1]), psytran(out_ystave - y[1]),
        psxtran(x[2]), psytran(out_ystave - y[2]));
  x += 3;
  y += 3;
  break;
  }

if (ps_gray != 0) ps_printf(" %f Sg", ps_gray);
if (thickness >= 0) ps_printf(" %f Slw S", thickness);
  else ps_printf(" F");
if (ps_gray != 0) ps_printf(" 0 Sg");
}




/*************************************************
*   Output and stroke or fill an absolute path   *
*************************************************/

/* This function (similar to the one above) is used for fancy slurs, when the
coordinate system has been rotated and translated so that its origin is at the
centre of the slur with the x axis joining the endpoints. The coordinates must 
therefore not use psxtran/psytran.

Arguments:
  x            vector of x coordinates
  y            vector of y coordinates
  c            vector of move/line/curve operators
  thickness    thickness of the lines for stroke; negative for fill only
  
Returns:       nothing    
*/

void 
ps_abspath(int *x, int *y, int *c, int thickness)
{
while (*c) switch(*c++)
  {
  case path_move:
  ps_printf(" %f %f Mt", *x++, *y++);
  break;

  case path_line:
  ps_printf(" %f %f Lt", *x++, *y++);
  break;

  case path_curve:
  ps_printf(" %f %f %f %f %f %f Ct", x[0], y[0], x[1], y[1], x[2], y[2]);
  x += 3;
  y += 3;
  break;
  }

if (ps_gray != 0) ps_printf(" %f Sg", ps_gray);
if (thickness >= 0) ps_printf(" %f Slw S", thickness);
  else ps_printf(" F");
if (ps_gray != 0) ps_printf(" 0 Sg");
}



/*************************************************
*         Output a PostScript string             *
*************************************************/

/* This function is called to output a user-supplied PostScript string.

Arguments:
  s           the string
  x           the x coordinate
  y           the y coordinate
  
Returns:      nothing
*/   

void 
ps_pstext(uschar *s, int x, int y)
{
ps_printf(" GS %f %f T\n%%User PostScript\n%s", psxtran(x), psytran(y), s);
fprintf(ps_file, "\n%%End user PostScript\nGR\n");
ps_chcount = 0;
}




/*************************************************
*            Set gray level                      *
*************************************************/

/* All that happens here is that the gray level is remembered for later use.

Argument:  the gray level
Returns:   nothing
*/

void 
ps_setgray(int gray)
{
ps_gray = gray;
}



/*************************************************
*            Set dash and capandjoin             *
*************************************************/

/* The set values are remembered so that repetition is avoided.

Arguments:
  dashlength    the dash length
  gaplength     the gap length
  caj           the cap-and-join value
  
Returns:        nothing
*/   

void 
ps_setdash(int dashlength, int gaplength, int caj)
{
if (dashlength != out_dashlength || gaplength != out_dashgaplength)
  {
  if (dashlength == 0 && gaplength == 0) ps_printf("[] 0 Sd");
    else ps_printf("[%f %f] 0 Sd", dashlength, gaplength);
  out_dashlength = dashlength;
  out_dashgaplength = gaplength;
  }

if (caj != ps_caj)
  {
  if ((caj & caj_round) == caj_round) ps_printf(" 1 Slc");
    else if ((caj & caj_square) == caj_square) ps_printf(" 2 Slc");
      else ps_printf(" 0 Slc");

  if ((caj & caj_round_join) == caj_round_join) ps_printf(" 1 Slj");
    else if ((caj & caj_bevel_join) == caj_bevel_join) ps_printf(" 2 Slj");
      else ps_printf(" 0 Slj");

  ps_caj = caj;
  }
}



/*************************************************
*            Gsave and Grestore                  *
*************************************************/

/* These functions are called from setslur.c when the coordinate system is 
translated and rotated for the drawing of a fancy slur. They translate directly 
into PostScript shorthand for gsave and grestore.

Arguments:  none
Returns:    nothing
*/

void 
ps_gsave(void)
{
ps_printf(" GS");
}

void 
ps_grestore(void)
{
ps_printf(" GR");
}



/*************************************************
*                 Rotate                         *
*************************************************/

/* This function rotates the coordinate system.

Argument:   the amount to rotate, in radians
Returns:    nothing
*/

void 
ps_rotate(double r)
{
if (r != 0.0) ps_printf(" %f R", (int)((r/(4.0 * atan(1.0)))*180000.0));
}


/*************************************************
*                  Translate                     *
*************************************************/

/* This function translates the coordinate system.

Arguments:
  x          x coordinate of the new origin
  y          y coordinate of the new origin
  
Returns:     nothing
*/  

void 
ps_translate(int x, int y)
{
ps_printf(" %f %f T", psxtran(x), psytran(out_ystave - y));
}



/*************************************************
*            Handle new movement                 *
*************************************************/

/* The only thing this function does is to compute the value of the lefthand
margin, which may change between movements. There is a fudge to the computation
for the default value, in order to keep it the same as it was before the
sheetwidth sizes were adjusted to be precisely the paper size, at least for A4
paper.

Arguments:   none
Returns:     nothing
*/

void 
ps_newmovt(void)
{
if (curmovt->leftmargin < 0)    /* Default (not set by user) */
  {
  ps_xmargin = (ps_sheetwidth - curmovt->linelength)/2 +
    13000000/(2*main_magnification);
  if (ps_xmargin < 20000) ps_xmargin = 20000;
  }
else ps_xmargin = curmovt->leftmargin;
}




/*************************************************
*       Start a given bar for a given stave      *
*************************************************/

/* Force a new line and output an identifying comment.

Arguments:
  barnumber    the bar number
  stave        the stave
*/    

void 
ps_startbar(int barnumber, int stave)
{
if (ps_chcount != 0) ps_chcount = BIGNUMBER/2;
ps_printf("%%%b/%d\n", barnumber, stave);
ps_chcount = 0;
}




/*************************************************
*     Include a file in the PostScript output    *
*************************************************/

/* This function is called for the main header file, and also for user-supplied
head/foot or setup files. Certain lines in the header are included only for EPS
files. They are flagged in the header file with %EPS. Otherwise, if a line
starts with %, it is copied only if it starts with %%. Blank lines are omitted.

Arguments:
  s           the file name
  relativize  if TRUE, relativize non-absolute path to the current input file 
  
Returns:      nothing 
*/

static void 
ps_include(uschar *s, BOOL relativize)
{
FILE *f;
uschar name[256];

Ustrcpy(name, s);
if (relativize) sys_relativize(name);
f = Ufopen(name, "r");

if (f != NULL)
  {
  uschar buff[256];
  while (Ufgets(buff, 256, f) != NULL)
    {
    if (ps_EPS && Ustrncmp(buff, "%EPS ", 5) == 0) Ufputs(buff+5, ps_file);
      else if (buff[0] != '\n' && (buff[0] != '%' || buff[1] == '%'))
        Ufputs(buff, ps_file);
    }
  if (buff[Ustrlen(buff)-1] != '\n') fputc('\n', ps_file);
  fclose(f);
  ps_chcount = 0;
  }

else error_moan(4, name, strerror(errno));
}



/*************************************************
*           Output PostScript head/foot          *
*************************************************/

/* Output literal PostScript for a heading/footing. If the first character is 
'<', the remainder is the name of a file. Otherwise, it is literal PostScript.

Argument:   pointer to a headstr
Returns:    nothing
*/

void 
ps_headfoot(headstr *p)
{
fprintf(ps_file, "\n%%User PostScript\n");
if (p->a.text[0] == '<') ps_include(p->a.text + 1, TRUE); else
  fprintf(ps_file, "%s\n", p->a.text);
fprintf(ps_file, "%%End user PostScript\n");
ps_chcount = 0;
}




/*************************************************
*        Include a Music font in the output      *
*************************************************/

/* This function is called if either PMW-Music or PMW-Alpha is to be included 
in the output.

Argument:  the name of the font
Returns:   nothing
*/

static void 
include_font(char *name)
{
FILE *f = NULL;
uschar buff[256];

/* First look in any additional directory */

if (ps_fontdir_extra != NULL)
  {
  sprintf(CS buff, "%s/%s", ps_fontdir_extra, name);
  f = Ufopen(buff, "r");
  }

/* Try the default directory if not yet found. */

if (f == NULL)
  {
  sprintf(CS buff, "%s/%s", ps_fontdir_default, name);
  f = Ufopen(buff, "r");
  if (f == NULL)                     /* These are hard errors */
    {
    if (ps_fontdir_extra == NULL)  
      error_moan(7, buff, strerror(errno));
    else
      error_moan(130, name, ps_fontdir_extra, ps_fontdir_default);
    }       
  } 

while (Ufgets(buff, sizeof(buff), f) != NULL)
  {
  if (Ustrncmp(buff, "%%BeginResource:", 16) == 0)
    {
    fprintf(ps_file, "%s", CS buff);
    break;
    }
  }

while (Ufgets(buff, sizeof(buff), f) != NULL)
  {
  fprintf(ps_file, "%s", CS buff);
  if (Ustrncmp(buff, "%%EndResource", 13) == 0) break;
  }
}




/*************************************************
*           Produce PostScript output            *
*************************************************/

/* This is the controlling function for generating PostScript output. If the
print_imposition has the special value pc_EPS, we are producing EPS PostScript,
and a number of page-related parameters are then ignored. 

Arguments: none
Returns:   nothing
*/

void 
ps_go(void)
{
BOOL used_pmw_music = FALSE;
BOOL used_pmw_alpha = FALSE;
time_t timer;
int i, w = 0, d = 0;
int count = 0;
int fcount = 1;
int scaled_main_sheetwidth = 
  mac_muldiv(main_sheetwidth, print_magnification, 1000);

/* Initialize the current page number and page list data */

ps_EPS = (print_imposition == pc_EPS);
print_setup_pagelist(ps_EPS? FALSE : print_reverse);

/* Set the top of page y coordinate; the PostScript is relative to the usual
bottom of page origin. Before the invention of the imposition parameter, we
computed this from the pagelength, but with some minima imposed. For
compatibility, keep this unchanged for cases when imposition is defaulted. For
EPS, we use the sheetsize, whatever it may be. */

if (ps_EPS) ps_ymax = main_truepagelength + 50000; else
  {
  if (opt_landscape)
    {
    if (main_truepagelength < 492000)
      ps_ymax = mac_muldiv(526000, 1000, print_magnification);
        else ps_ymax = main_truepagelength + 34000;
    }
  else
    {
    if (main_truepagelength < 720000)
      ps_ymax = mac_muldiv(770000, 1000, print_magnification);
        else ps_ymax = main_truepagelength + 50000;
    }

  /* Take the opportunity of setting true paper sizes for imposing */

  switch(print_imposition)
    {
    case pc_a5ona4:
    w = 595000;
    d = 842000;
    ps_ymax = main_truepagelength + 50000;
    break;

    case pc_a4ona3:
    w = 842000;
    d = 1190000;
    ps_ymax = main_truepagelength + 50000;
    break;
    }
  }

/* Adjust paper size to the magnification */

ps_sheetwidth = mac_muldiv(main_sheetwidth, 1000, main_magnification);
ps_ymax = mac_muldiv(ps_ymax, 1000, main_magnification);

/* Initializing stuff at the start of the PostScript file. We are attempting to
keep to the 3.0 structuring conventions. Initial comments ("header") come
first. */

time (&timer);
fprintf(ps_file, "%%!PS-Adobe-3.0%s\n", ps_EPS? " EPSF-3.0" : "");
fprintf(ps_file, "%%%%Creator: Philip's Music Writer %s\n", version_string);
fprintf(ps_file, "%%%%CreationDate: %s", ctime(&timer));
if (ps_EPS) fprintf(ps_file, "%%%%BoundingBox: (atend)\n");
  else fprintf(ps_file, "%%%%Pages: (atend)\n");
fprintf(ps_file, "%%%%DocumentNeededResources: font ");

for (i = 0; i < font_tablen; i++)
  {
  int j;
  for (j = 0; j < i; j++) if (font_table[i] == font_table[j]) break;
  if (j == i)
    {
    if (++fcount > 3)
      {
      fprintf(ps_file, "\n%%%%+ font ");
      fcount = 1;
      }
    fprintf(ps_file, "%s ", font_List[font_table[i]].psname);
    /* Remember which music fonts have been used */
    if (Ustrcmp(font_List[font_table[i]].psname, "PMW-Music") == 0)
      used_pmw_music = TRUE;
    if (Ustrcmp(font_List[font_table[i]].psname, "PMW-Alpha") == 0)
      used_pmw_alpha = TRUE;
    }
  ps_IdStrings[i] = font_IdStrings[j];
  }
fprintf(ps_file, "\n");

if (output_includefont && (used_pmw_music || used_pmw_alpha))
  {
  fprintf(ps_file, "%%%%DocumentSuppliedResources: font%s%s\n",
    used_pmw_music? " PMW-Music":"", used_pmw_alpha? " PMW-Alpha":"");
  }

if (!ps_EPS) fprintf(ps_file, 
  "%%%%Requirements: numcopies(%d)\n", output_copies);
fprintf(ps_file, "%%%%EndComments\n\n");

/* Deal with a known paper size */

switch (opt_sheetsize)
  {
  case sheet_A3:
  fprintf(ps_file, "%%%%BeginPaperSize: a3\na3\n%%%%EndPaperSize\n\n");
  break;  
  
  case sheet_A4:
  fprintf(ps_file, "%%%%BeginPaperSize: a4\na4\n%%%%EndPaperSize\n\n");
  break;
   
  case sheet_A5:
  fprintf(ps_file, "%%%%BeginPaperSize: a5\na5\n%%%%EndPaperSize\n\n");
  break;
  
  case sheet_B5:
  fprintf(ps_file, "%%%%BeginPaperSize: b5\nb5\n%%%%EndPaperSize\n\n");
  break;
  
  case sheet_letter:
  fprintf(ps_file, "%%%%BeginPaperSize: letter\nletter\n%%%%EndPaperSize\n\n");
  break;
  
  default:
  break; 
  }      

/* Next, the file's prologue */

fprintf(ps_file, "%%%%BeginProlog\n");

/* If there is a header file, copy it now. Its name is NOT relative to the
main input file. (If it is not absolute, it is taken relative to the current 
directory.) */

if (ps_header != NULL) ps_include(ps_header, FALSE);

/* Deal with any requested PostScript setup */

if (main_pssetup != NULL)
  {
  headstr *h = main_pssetup;
  fprintf(ps_file, "\n%% Included pssetup strings and/or files\n");
  while (h != NULL)
    {
    if (h->a.text[0] == '<') ps_include(h->a.text + 1, TRUE);
      else fprintf(ps_file, "%s\n", h->a.text);
    h = h->next;
    }
  fprintf(ps_file, "\n");
  }

fprintf(ps_file, "%%%%EndProlog\n\n");

/* The setup section sets up the printing device. We include the font finding
in here, as it seems the right place. If output_includefont is set, include the
music font(s) in the output file. */

fprintf(ps_file, "%%%%BeginSetup\n");

if (output_includefont)
  {
  if (used_pmw_music) include_font("/PMW-Music.pfa");
  if (used_pmw_alpha) include_font("/PMW-Alpha");
  }

/* Now list the other fonts */

for (i = 0; i < font_tablen; i++)
  {
  int j;
  for (j = 0; j < i; j++) if (font_table[i] == font_table[j]) break;
  if (j == i)
    {
    fontstr *f = font_List + font_table[i];
    uschar *s = f->psname;
    fprintf(ps_file, "%%%%IncludeResource: font %s\n", s);
    fprintf(ps_file, "/%s /%sX /%s inf\n", font_IdStrings[i],
      font_IdStrings[i], s);
    }
  }

/* Unless EPS, we used to select A4 paper, but only once (to allow concatenated
files). However, this seems to give trouble with Ghostview for doing magnify
windows, and it doesn't seem to affect modern PostScript printers anyway. So it
is no longer done.

Select the number of copies if not 1, set manual feed if the flag is set, deal
with duplex and tumble options, and end the setup section. */

if (!ps_EPS)
  {
  /*********
  fprintf(ps_file,
    "currentdict /a4_done known not {a4 /a4_done true def} if\n");
  **********/

  if (output_copies != 1) fprintf(ps_file, "/#copies %d def\n", output_copies);
  if (output_manualfeed || output_duplex)
    {
    fprintf(ps_file, "statusdict begin");
    if (output_manualfeed) fprintf(ps_file, " /manualfeed true def");
    if (output_duplex)
      {
      fprintf(ps_file, " true setduplexmode");
      if (output_tumble) fprintf(ps_file, " true settumble");
      }
    fprintf(ps_file, " end\n");
    }
  }

fprintf(ps_file, "%%%%EndSetup\n\n");

/* Now the requested pages. The print_nextpage function returns one or two
pages. When printing 2-up either one of them may be null. */

for (;;)
  {
  int scaled = 1000;
  BOOL recto = FALSE;

  sysblock *s;
  pagestr *ps_1stpage, *ps_2ndpage;

  if (!print_nextpage(&ps_1stpage, &ps_2ndpage)) break;
  if (ps_1stpage != NULL && ps_2ndpage != NULL)
    fprintf(ps_file, "%%%%Page: %d&%d %d\n", ps_1stpage->number,
      ps_2ndpage->number, ++count);
  else if (ps_1stpage != NULL)
    {
    fprintf(ps_file, "%%%%Page: %d %d\n", ps_1stpage->number, ++count);
    recto = (ps_1stpage->number & 1) != 0;
    }
  else
    {
    fprintf(ps_file, "%%%%Page: %d %d\n", ps_2ndpage->number, ++count);
    recto = (ps_2ndpage->number & 1) != 0;
    }

  fprintf(ps_file, "%%%%BeginPageSetup\n/pagesave save def\n");

  ps_curfont = -1;
  ps_curfontX = FALSE;
  ps_chcount = 0;
  ps_caj = 0;

  if (ps_EPS)
    {
    if (main_righttoleft)
      ps_printf("%f 0 T -1 1 scale\n", main_sheetwidth); 
    if (main_magnification != 1000)
      ps_printf("%f dup scale\n", main_magnification);
    }
  else
    {
    if (main_righttoleft)
      {
      pagestr *temp = ps_1stpage;
      ps_1stpage = ps_2ndpage;
      ps_2ndpage = temp;
      }      
     
    /* Move the origin to the desired position. The values 1 (upright, 1-up,
    portrait), 2 (sideways, 2-up, portrait), and 4 (sideways, 1-up, landscape)
    use bottom left, i.e. no translation, but we have to generate an adjustment
    for type 2 if sheetwidth isn't half the paper size.

    The gutter facility is available only when printing 1-up. */

    switch (print_pageorigin)
      {
      case 0: /* A4 Sideways, 1-up, portrait */
      ps_printf("0 %f T -90 R\n", 595000);
      if (print_gutter != 0)
        ps_printf("%f 0 T\n", recto? print_gutter : -print_gutter);
      break;

      case 1: /* Upright, 1-up, portrait */
      if (print_gutter != 0)
        ps_printf("%f 0 T\n", recto? print_gutter : -print_gutter);
      break;

      case 2: /* Sideways, 2-up, portrait */
      if (d/2 != scaled_main_sheetwidth)
        ps_printf("%f 0 T\n",
          (d/2 - scaled_main_sheetwidth)/(print_pamphlet? 1:2));
      break;

      case 3: /* Upright, 2-up, portrait */
      ps_printf("0 %f T -90 R\n",
        d - (d/2 - scaled_main_sheetwidth)/(print_pamphlet? 1:2));
      break;

      case 4: /* A4 Sideways, 1-up, landscape */
      if (print_gutter != 0)
        ps_printf("%f 0 T\n", recto? print_gutter : -print_gutter);
      break;

      case 5: /* Upright, 1-up, landscape; page size defined by sheetsize */
              /* Sheetwidth is original sheet height */
      ps_printf("0 %f T -90 R\n", scaled_main_sheetwidth); 
      break;

      case 6: /* A4 Sideways, 2-up, landscape */
      ps_printf("%f %f T -90 R\n", d/2, w);
      break;

      case 7: /* Upright, 2-up, landscape */
      ps_printf("0 %f T\n", d/2);
      break;
      }

    if (print_image_xadjust != 0 || print_image_yadjust != 0)
      ps_printf("%f %f T\n", print_image_xadjust, print_image_yadjust);

    if (main_righttoleft)
      ps_printf("%f 0 T -1 1 scale\n", scaled_main_sheetwidth); 

    if (main_magnification != 1000 || print_magnification != 1000)
      {
      scaled = mac_muldiv(main_magnification, print_magnification, 1000);
      ps_printf("%f dup scale\n", scaled);
      }
    }
 
  /* End of setup */

  fprintf(ps_file, "%%%%EndPageSetup\n");
  s = curpage->sysblocks;
  if (s != NULL) curmovt = s->movt;
  ps_newmovt();

  /* When printing 2-up, we may get one or both pages; when not printing 2-up,
  we may get either page given, but not both. */

  if (ps_1stpage != NULL)
    {
    curpage = ps_1stpage;
    out_page();
    }

  if (ps_2ndpage != NULL)
    {
    if (ps_chcount > 0) fprintf(ps_file, "\n");
    if (print_imposition == pc_a5ona4 || print_imposition == pc_a4ona3)
      {
      int sign = main_righttoleft? -1 : +1; 
      int dd = mac_muldiv(d, 500, scaled);
      if (opt_landscape) ps_printf("0 %f T\n", -dd); else
        ps_printf("%f 0 T\n", sign * (print_pamphlet? 
          mac_muldiv(main_sheetwidth, 1000, main_magnification) : dd));
      }
    curpage = ps_2ndpage;
    out_page();
    }

  /* EPS files are permitted to contain showpage, and this is actually useful
  because it means an EPS file can be printed or displayed. So we don't cut out
  showpage. */

  fprintf(ps_file, "\npagesave restore showpage\n\n");
  }

/* Do PostScript trailer */

fprintf(ps_file, "%%%%Trailer\n");

if (ps_EPS)
  {
  if (main_righttoleft) 
    ps_printf("%%%%BoundingBox: %f %f %f %f\n",
      main_sheetwidth - 
        mac_muldiv(psxtran(out_bbox[2]), main_magnification, 1000),
      mac_muldiv(psytran(out_bbox[1]), main_magnification, 1000),
      main_sheetwidth - 
        mac_muldiv(psxtran(out_bbox[0]), main_magnification, 1000),
      mac_muldiv(psytran(out_bbox[3]), main_magnification, 1000));
  else
    ps_printf("%%%%BoundingBox: %f %f %f %f\n",
      mac_muldiv(psxtran(out_bbox[0]), main_magnification, 1000),
      mac_muldiv(psytran(out_bbox[1]), main_magnification, 1000),
      mac_muldiv(psxtran(out_bbox[2]), main_magnification, 1000),
      mac_muldiv(psytran(out_bbox[3]), main_magnification, 1000));
  }   
else fprintf(ps_file, "%%%%Pages: %d\n", count);
}

/* End of ps.c */
