chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / print.c
diff --git a/mup/mup/print.c b/mup/mup/print.c
new file mode 100644 (file)
index 0000000..ed1cb76
--- /dev/null
@@ -0,0 +1,4089 @@
+
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* make the final pass through the main list, writing out the PostScript output
+ * for printing the music. This file contains most of the general print
+ * phase functions.  Those that are associated with printing
+ * things from S_STAFF structs are mostly in prntdata.c.
+ * Tablature is printed via printtab.c.
+ * Additional print functions are in prntmisc.c and utils.c.
+ */
+
+#include <time.h>
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+
+/* print only if flag is turned on. This allows printing selected pages */
+static int Printflag = YES;
+#define OUTP(x)        if (Printflag==YES){(void)printf x;}
+#define OUTPCH(x) if (Printflag == YES){putchar x;}
+
+
+/* the PostScript commands */
+#define O_FONT         (1)
+#define O_SIZE         (2)
+#define O_LINEWIDTH    (3)
+#define O_CURVETO      (4)
+#define O_DOTTED       (5)
+#define O_SHOWPAGE     (6)
+#define O_SHOW                 (7)
+#define O_STAFF                (8)
+#define O_SETFONT      (9)
+#define O_MOVETO       (10)
+#define O_LINE         (11)            /* lineto stroke */
+#define O_BRACE                (12)
+#define O_BRACKET      (13)
+#define O_ENDDOTTED    (14)
+#define O_WAVY         (15)
+#define O_DASHED       (16)
+#define O_SAVE         (17)
+#define O_RESTORE      (18)
+#define O_FILL         (19)
+#define O_LINETO       (20)
+#define O_STROKE       (21)
+#define O_NEWPATH      (22)
+#define O_CLOSEPATH    (23)
+#define O_GSAVE                (24)
+#define O_GRESTORE     (25)
+#define O_CONCAT       (26)
+#define O_ARC          (27)
+#define O_EOFILL       (28)
+#define O_SCALE                (29)
+#define O_TRANSLATE    (30)
+#define O_ROTATE       (31)
+#define O_WIDTHSHOW    (32)
+#define O_ROLL         (33)
+
+#ifdef __TURBOC__
+#define SMALLMEMORY 1
+#endif
+
+/*
+ * For debugging, it can be useful to display the "bounding box"
+ * which is stored in the coordinate arrays of various entities.
+ * This list tells which entities have coords.
+ * These must match the indices in the Bbox_list array.
+ */
+#define BB_BAR         0
+#define BB_CHORD       1
+#define BB_FEED                2
+#define BB_GRPSYL      3
+#define BB_BLOCKHEAD   4
+#define BB_NOTE                5
+#define BB_STAFF       6
+#define BB_STUFF       7
+/* Macros to turn on display of a coord type and to check if it is on */
+#define BB_SET(x)      Do_bbox |= (1<<x)
+#define BB_IS_SET(x)   (Do_bbox & (1<<x))
+
+/* This struct holds information about how to display a coord bounding box.
+ * We use the environment variable MUP_BB to turn on this displaying.
+ * For now, we hard-code what color/dashing to use. MUP_BB could optionally
+ * contain color info some day, but that seems like overkill flexibility.
+ */
+static struct Bbox {
+       char    id;     /* character in $MUP_BB that says to draw these */
+       char    red;    /* per cent of this color to use when drawing */
+       char    green;  
+       char    blue;
+       char    dash_on;        /* for setdash */
+       char    dash_off;
+} Bbox_list[] = {
+       { 'b',  100,    50,     0,      5,      2 },    /* BAR */
+       { 'c',  0,      80,     0,      5,      2 },    /* CHORD */
+       { 'f',  50,     50,     0,      0,      0 },    /* FEED */
+       { 'g',  100,    0,      0,      0,      0 },    /* GRPSYL */
+       { 'h',  0,      80,     50,     0,      0 },    /* headings, etc */
+       { 'n',  0,      0,      100,    0,      0 },    /* NOTE */
+       { 's',  0,      50,     80,     5,      3 },    /* STAFF */
+       { 'u',  100,    0,      100,    5,      2 }     /* STUFF */
+};
+static short Do_bbox = 0;
+
+#ifdef SMALLMEMORY
+/* if memory is scarce, we do each font in a separate save/restore context.
+ * Need flag to keep track of whether we are in one of those */
+static int Did_save = NO;
+#endif
+
+/* for header, to indicate when output file was generated */
+static char *Dayofweek[] = {
+       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static char *Month[] = {
+       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static int Pagesprinted = 0;           /* number of pages actually printed */
+static int Feednumber;                 /* how many pagefeeds we've handled */
+
+
+static struct STAFF *Last_staff;       /* point to last STAFF we saw, to
+                                        * save having to back up to it
+                                        * later to check if it was a
+                                        * multirest. */
+
+static int Last_linetype = -1;         /* keep track of last type. If same,
+                                        * this time, no reason to output */
+static float Last_staffscale;          /* Change in staffscale also changes
+                                        * line width, so have to remember it */
+static short Doing_dotted = NO;                /* keep track of whether last line
+                                        * was dotted. If it was, but current
+                                        * one isn't, need to tell PostScript */
+static double x1a, x2a, ya;            /* x and y adjustments */
+static int sn;
+extern int *Check_p;
+static int Landscape;                  /* how much to translate for landscape
+                                        * mode, or 0 if not in landscape */
+
+/* static functions */
+static void init4print P((void));
+static void page1setup P((void));
+static int use_landscape P((double pgwidth, double pgheight));
+static void setup_user_fonts P((void));
+#ifdef EXTCHAR
+static void setup_extended_fonts P((void));
+#endif
+static void pr_line P((struct LINE *line_p, char *fname, int lineno));
+static void dr_line P((double x1, double y1, double x2, double y2, int ltype));
+static void pr_curve P((struct CURVE *curve_p, char *fname, int lineno));
+static void outp_muschar P((double x, double y, int ch, int size, int font));
+static void pr_ital_muschar P((double x, double y, int ch, int size, int font));
+static void pr_bar P((struct MAINLL *mll_p, double x, int is_pseudobar));
+static void pr_bar_range P((struct BAR *bar_p, int topstaff,
+               int botstaff, double x, int next_is_restart,
+               struct MAINLL *mll_p));
+static void draw_bar P((int bartype, int linetype, double x, double y1,
+               double y2));
+static void pr_repeat_dots P((int bartype, int staff, double x));
+static void do_rdots P((double x, double y, double topoffset, double bottomosffset));
+static void pr_reh P((struct MAINLL *mll_p));
+static void pr_box P((double x, double y, double boxheight, double boxwidth));
+static void pr_topbot P((struct BLOCKHEAD *blockhead_p, double y));
+static void pr_restarts P((struct MAINLL *mll_p, double y1, double y2,
+               int need_vert_line));
+static void outint P((int val));
+static void pr_wstring P((double x, double y, char *string, int justify,
+               double fullwidth, char * fname, int lineno));
+static void outstring P((double x, double y, double fullwidth, char *string,
+               char * fname, int lineno));
+static int begin_string P((int in_string));
+static int end_string P((int in_string, double space_adjust));
+static void outop P((int op));
+static void pr_headfoot P((struct MAINLL *mll_p));
+static void to_next_page P((struct MAINLL *mll_p));
+static void pr_print P((struct PRINTDATA *printdata_p));
+static double pr_keysig P((int staffno, int sharps, int naturals, double x,
+               int really_print));
+static void draw_keysig P((int muschar, int symbols, double x,
+               double y, int *table, int offset, int skip));
+static double pr_timesig P((int staffno, double x, int multnum,
+               int really_print));
+static double tsjam P((int num));
+static void pr_tsnum P((double x, double y, char *str, double jam));
+static void draw_circle P((double x, double y, double radius));
+static void do_scale P((double xscale, double yscale));
+static void pr_font P((int font, int size));
+static void prfontname P((int font));
+static void split_a_string P((double x, double y, char *string, int justify,
+               double fullwidth, char *fname, int lineno));
+static void j_outstring P((double x, double y, char *string, int justify,
+               double fullwidth, char *fname, int lineno));
+static void set_staff_y P((struct MAINLL *main_p));
+static void pr_meas_num P((int staffno, double x));
+static void setscale P((void));
+static void show_coord P((float *coord_p, int index));
+static void prep_bbox P((void));
+static void show_bounding_boxes P((struct MAINLL *mll_p));
+\f
+
+/* main function of print phase. Walk through main list,
+ * printing things as we go */
+
+void
+print_music()
+
+{
+       struct MAINLL *mll_p;   /* to walk through list */
+       struct FEED *feed_p;
+
+
+       debug(256, "print_music");
+       prep_bbox();
+
+       /* initialize for printing */
+       init4print();
+
+       /* walk down the list, printing as we go */
+       for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL;
+                                               mll_p = mll_p->next) {
+
+               {
+                       /* in debug mode, print out Postscript comments
+                        * to make it easier to map output back to input */
+                       OUTP(("%%  %s\n", stype_name(mll_p->str)));
+               }
+
+               /* tell output program what the user input line was */
+               /* STAFF structs have lots of things hung off them that
+                * could come from different input lines, so do that
+                * separately */
+               if (mll_p->str != S_STAFF && mll_p->inputlineno > 0) {
+                       pr_linenum(mll_p->inputfile, mll_p->inputlineno);
+               }
+
+               /* call appropriate function(s) based on type */
+               switch(mll_p->str) {
+
+               case S_SSV:
+                       /* assign the values from the SSV and reset
+                        * the _win parameters */
+                       asgnssv(mll_p->u.ssv_p);
+                       set_win(PGHEIGHT - EFF_TOPMARGIN, EFF_BOTMARGIN,
+                                       PGWIDTH - eff_rightmargin((struct MAINLL *)0),
+                                       eff_leftmargin((struct MAINLL *)0));
+                       break;
+
+               case S_STAFF:
+                       OUTP(("%% staff %d\n", mll_p->u.staff_p->staffno));
+                       outop(O_SAVE);
+                       set_staffscale(mll_p->u.staff_p->staffno);
+                       pr_staff(mll_p);
+                       outop(O_RESTORE);
+                       Curr_font = FONT_UNKNOWN;
+                       Curr_size = DFLT_SIZE;
+                       Last_staff = mll_p->u.staff_p;
+                       break;
+
+               case S_BAR:
+                       pr_bar(mll_p, (double)mll_p->u.bar_p->c[AX],
+                                                               NO);
+                       /* reset the staffscale to its scorewide
+                        * default value */
+                       set_staffscale(0);
+                       break;
+
+               case S_CHHEAD:
+                       /* nothing to do here--we print notes when we
+                        * hit the S_STAFF structs */
+                       break;
+
+               case S_PRHEAD:
+                       pr_print(mll_p->u.prhead_p->printdata_p);
+                       break;
+
+               case S_LINE:
+                       pr_line(mll_p->u.line_p, mll_p->inputfile,
+                                                       mll_p->inputlineno);
+                       break;
+
+               case S_CURVE:
+                       pr_curve(mll_p->u.curve_p, mll_p->inputfile,
+                                                       mll_p->inputlineno);
+                       break;
+
+               case S_FEED:
+                       pr_feed(mll_p);
+                       pr_endings(mll_p);
+                       break;
+
+               case S_CLEFSIG:
+                       (void) pr_clefsig(mll_p, mll_p->u.clefsig_p, YES);
+                       /* have to do rehearsal marks, because we only
+                        * print a pseudo-bar if it is visible */
+                       if (mll_p->u.clefsig_p->bar_p != (struct BAR *) 0) {
+                               pr_reh(mll_p);
+                       }
+                       break;
+
+               case S_BLOCKHEAD:
+                       if (mll_p->prev == 0 || mll_p->prev->str != S_FEED) {
+                               pfatal("blockhead without preceeding feed");
+                       }
+                       feed_p = mll_p->prev->u.feed_p;
+                       set_win_coord(mll_p->u.blockhead_p->c);
+                       set_win(feed_p->c[AN], feed_p->c[AS],
+                                       feed_p->c[AE], feed_p->c[AW]);
+                       set_cur(mll_p->prev->u.feed_p->c[AW],
+                                       mll_p->prev->u.feed_p->c[AN]);
+                       pr_print(mll_p->u.blockhead_p->printdata_p);
+                       set_win_coord(0);
+                       break;
+               default:
+                       pfatal("unknown item in main list");
+                       break;
+               }
+       }
+
+       /* do grid atend things if necessary */
+       if (Atend_info.grids_used > 0) {
+               if (Atend_info.separate_page == YES) {
+                       /* The only MUP_BB thing that matters on grids at end
+                        * page is header/footer, and because of the order
+                        * in which things are done in pr_atend()
+                        * (i.e., when top/bottom are set relative to when the
+                        * MUP_BB printing code is called),
+                        * it's hard to find a way
+                        * to make that work without breaking something else.
+                        * So since grids at end is such a rare case,
+                        * and MUP_BB is just for debugging,
+                        * we just turn it off. */
+                       Do_bbox = 0;
+               }
+               pr_atend();
+       }
+
+       /* do final stuff for last page */
+       pr_headfoot(Mainlltc_p);
+}
+\f
+
+/* do the things for starting a new page */
+void
+newpage(mll_p)
+
+struct MAINLL *mll_p;
+
+{
+       pr_headfoot(mll_p);
+       Pagenum++;
+       to_next_page(mll_p);
+}
+\f
+
+/* print final trailer */
+
+void
+trailer()
+
+{
+       int f;                  /* font index */
+
+       Printflag = YES;
+       printf("%%%%Trailer\n");
+       printf("%%%%DocumentFonts: ");
+       for (f = 1; f < MAXFONTS; f++) {
+               if (Font_used[f] == YES) {
+                       prfontname(f);
+               }
+       }
+       
+       printf("\n%%%%Pages: %d\n", Score.panelsperpage == 1 ? Pagesprinted :
+                               ((Pagesprinted + 1) / 2) );
+}
+\f
+
+/* initialize things for print pass through main list */
+
+static void
+init4print()
+
+{
+       struct tm *timeinfo_p;
+       time_t clockinfo;
+       struct MAINLL *mll_p;
+       char *bbox_format;
+       static int first_time = YES;
+
+       if (first_time == NO) {
+               page1setup();
+               return;
+       }
+       first_time = NO;
+
+       /* initialize the SSV data */
+       initstructs();
+
+       printf("%%!PS-Adobe-1.0\n");
+       printf("%%%%Creator: Mup\n");
+       printf("%%%%Title: music: %s from %s\n", Outfilename, Curr_filename);
+       clockinfo = time((time_t *)0);
+       timeinfo_p = localtime(&clockinfo);
+       printf("%%%%CreationDate: %s %s %d %d:%d:%d %d\n",
+                       Dayofweek[timeinfo_p->tm_wday],
+                       Month[timeinfo_p->tm_mon], timeinfo_p->tm_mday,
+                       timeinfo_p->tm_hour, timeinfo_p->tm_min,
+                       timeinfo_p->tm_sec, 1900 + timeinfo_p->tm_year);
+       printf("%%%%Pages: (atend)\n");
+       printf("%%%%DocumentFonts: (atend)\n");
+       /* we need to know the value of panelsperpage before setting up the
+        * first page, as well as the pagewidth and pageheight,
+        * so need to peek into main list up till the first non-SSV
+        * to get that. */
+       for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL;
+                                               mll_p = mll_p->next) {
+               if (mll_p->str == S_SSV) {
+                       asgnssv(mll_p->u.ssv_p);
+               }
+               else {
+                       /* as soon as we hit something other than SSV,
+                        * we're past any page size changes */
+                       break;
+               }
+       }
+       bbox_format = "%%%%BoundingBox: 0 0 %d %d\n";
+       if (Score.panelsperpage == 2) {
+               /* have to compensate for the fact that our page width/height
+                * internally are that of the panel, but here we need the
+                * physical paper size */
+               printf(bbox_format, (int) (Score.pageheight * PPI),
+                               (int) (Score.pagewidth * 2.0 * PPI));
+       }
+       else if ((Landscape = use_landscape(Score.pagewidth, Score.pageheight))
+                                                               != 0) {
+               printf(bbox_format, (int) (Score.pageheight * PPI),
+                               (int) (Score.pagewidth * PPI));
+       }
+       else {
+               printf(bbox_format, (int) (Score.pagewidth * PPI),
+                               (int) (Score.pageheight * PPI));
+       }
+       printf("%%%%EndComments\n");
+       ps_prolog();
+       printf("/flagsep %.2f 300 mul def\t %% %.2f stepsizes\n",
+                               FLAGSEP / STEPSIZE, FLAGSEP / STEPSIZE);
+       srand((unsigned)timeinfo_p->tm_sec);
+       (void) printf("/scv %d def ", ((rand() & 0x27d) << 8) | 4);
+       (void) printf("/sf 962 string def\n");
+       (void) printf("/fa {/p 0 def /chr exch -3 bitshift 127 and def {sf exch p add dup /p exch def chr put} forall} def\n");
+       (void) printf("[ 74 62 70 54 29 55 36 37 19 26 45 40 41 50 45 52 19 73 11 68 ] 1567304784 fa\n");
+       (void) printf("[ 961 ] 1341740116 fa\n");
+       (void) printf("[ 12 4 5 4 4 2 4 3 3 7 7 3 5 5 4 5 4 2 5 3 ] 1969419526 fa\n");
+       (void) printf("[ 96 4 4 3 2 4 11 2 6 23 13 16 8 3 28 13 8 3 6 11 ] 387152134 fa\n");
+       (void) printf("[ 268 4 13 12 5 4 4 5 4 5 3 2 4 3 4 8 3 3 9 2 ] 305899779 fa\n");
+       (void) printf("[ 369 3 4 9 3 3 9 2 4 3 4 7 4 4 4 9 5 3 5 4 ] 477458695 fa\n");
+       (void) printf("[ 498 4 36 4 37 4 42 4 4 37 4 4 35 4 4 5 13 3 4 4 ] 1130513667 fa\n");
+       (void) printf("[ 759 3 5 33 4 5 9 29 5 4 3 5 4 4 5 4 4 5 4 3 ] 1205319942 fa\n");
+       (void) printf("[ 902 8 4 2 3 4 3 4 4 3 2 3 9 ] 1708988675 fa\n");
+       (void) printf("[ 468 6 4 10 3 30 5 3 24 40 4 3 3 3 3 8 23 1 1 1 ] 123455756 fa\n");
+       (void) printf("[ 664 23 4 2 13 66 4 5 9 ] 2061720845 fa\n");
+       (void) printf("[ 795 ] 1622189328 fa\n");
+       (void) printf("[ 463 45 40 41 50 45 84 ] 304180545 fa\n");
+       (void) printf("[ 494 40 41 49 45 43 84 ] 251711819 fa\n");
+       (void) printf("[ 149 203 37 144 ] 358262127 fa\n");
+       (void) printf("[ 456 142 52 ] 95949173 fa\n");
+       (void) printf("[ 0 13 13 10 65 36 6 26 38 17 13 53 4 13 13 25 36 183 7 140 ] 1751712121 fa\n");
+       (void) printf("[ 839 5 13 12 13 13 48 ] 1943250302 fa\n");
+       (void) printf("[ 30 164 254 7 42 4 36 4 18 1 18 4 46 3 1 41 4 39 4 41 ] 499619205 fa\n");
+       (void) printf("[ 798 1 3 1 ] 1277775234 fa\n");
+       (void) printf("[ 76 32 135 79 99 8 246 43 30 160 ] 734015880 fa\n");
+       (void) printf("[ 265 70 36 12 25 87 4 36 4 37 4 46 4 41 43 83 4 83 41 3 ] 1546658194 fa\n");
+       (void) printf("[ 193 49 180 8 17 134 ] 831070621 fa\n");
+       (void) printf("[ 353 366 ] 1033403809 fa\n");
+       (void) printf("[ 266 1 190 39 40 41 50 45 43 45 ] 1758436783 fa\n");
+       (void) printf("[ 423 8 109 ] 508918194 fa\n");
+       (void) printf("[ 328 6 30 6 31 6 269 ] 212071871 fa\n");
+       (void) printf("[ 390 357 2 ] 1671244225 fa\n");
+       (void) printf("[ 500 ] 347047368 fa\n");
+       (void) printf("[ 558 ] 1276946910 fa\n");
+       (void) printf("[ 651 ] 2109048312 fa\n");
+       (void) printf("[ 644 ] 1914352160 fa\n");
+       (void) printf("[ 520 ] 471204394 fa\n");
+       (void) printf("[ 512 5 2 ] 1930983991 fa\n");
+       (void) printf("[ 665 ] 154021439 fa\n");
+       (void) printf("[ 513 ] 777103941 fa\n");
+       (void) printf("[ 514 ] 260959830 fa\n");
+       (void) printf("[ 530 239 ] 1284535922 fa\n");
+       (void) printf("[ 510 ] 1982423675 fa\n");
+       (void) printf("[ 150 ] 1969948305 fa\n");
+       (void) printf("[ 511 7 134 ] 1407991454 fa\n");
+       (void) printf("[ 144 371 ] 1896661664 fa\n");
+       (void) printf("[ 464 52 ] 1444653737 fa\n");
+       (void) printf("[ 509 81 ] 1712172720 fa\n");
+       (void) printf("[ 110 11 32 24 22 18 40 12 54 7 17 19 18 19 22 13 377 94 9 11 ] 889612 fa\n");
+       (void) printf("[ 954 ] 1802916616 fa\n");
+       (void) printf("[ 80 146 51 78 37 84 8 8 73 5 44 45 33 9 73 9 130 9 11 12 ] 1808121621 fa\n");
+       (void) printf("[ 19 42 3 22 8 82 63 23 25 13 8 5 176 248 40 73 12 13 13 12 ] 1752602397 fa\n");
+       (void) printf("[ 22 10 37 42 1 2 19 26 6 38 17 13 38 11 21 13 16 9 27 9 ] 1598682919 fa\n");
+       (void) printf("[ 405 9 13 46 49 50 50 213 18 12 13 13 12 45 10 ] 160257827 fa\n");
+       (void) printf("[ 1 8 8 6 10 10 16 11 14 8 23 19 13 19 13 7 15 3 9 8 ] 882894639 fa\n");
+       (void) printf("[ 234 40 9 15 6 7 6 25 36 37 19 6 47 16 40 41 50 45 43 6 ] 185215791 fa\n");
+       (void) printf("[ 733 19 37 16 12 13 3 3 12 6 6 6 7 6 7 6 6 6 45 10 ] 1706915629 fa\n");
+       (void) printf("[ 24 10 37 45 2 17 5 1 15 4 7 5 8 8 17 17 13 11 8 26 ] 1713964852 fa\n");
+       (void) printf("[ 284 21 13 25 18 18 19 18 28 1 7 28 2 4 106 24 3 2 32 36 ] 1218620208 fa\n");
+       (void) printf("[ 695 62 1 7 13 1 7 2 37 4 8 5 13 12 13 13 12 45 5 1 ] 1317868340 fa\n");
+       (void) printf("[ 960 ] 75399990 fa\n");
+       (void) printf("[ 45 9 155 6 245 68 21 98 60 109 ] 1430691640 fa\n");
+       (void) printf("[ 20 27 15 25 8 33 173 13 45 37 83 170 5 34 8 115 40 12 13 13 ] 841629509 fa\n");
+       (void) printf("[ 901 ] 422446918 fa\n");
+       (void) printf("[ 27 25 37 13 3 40 12 73 49 77 4 33 4 68 89 219 21 27 3 4 ] 560155470 fa\n");
+       (void) printf("[ 466 6 135 41 7 6 36 6 89 ] 803193686 fa\n");
+       (void) printf("[ 42 80 1 55 80 1 80 36 37 155 1 263 40 65 ] 189315943 fa\n");
+       (void) printf("[ 6 31 36 9 43 21 6 185 36 37 210 ] 1031359337 fa\n");
+       (void) printf("[ 44 9 101 4 4 20 8 80 3 23 30 5 19 17 20 17 15 7 7 36 ] 586694517 fa\n");
+       (void) printf("[ 552 22 20 16 3 55 42 31 10 33 ] 343336822 fa\n");
+       (void) printf("[ 7 4 54 54 10 22 10 20 8 8 53 5 226 12 115 38 17 42 26 13 ] 1808462718 fa\n");
+       (void) printf("[ 780 32 ] 847653755 fa\n");
+       (void) printf("[ 3 63 31 408 18 4 18 6 22 13 15 3 32 9 17 4 15 5 18 4 ] 1627872128 fa\n");
+       (void) printf("[ 724 83 7 ] 1643402114 fa\n");
+       (void) printf("[ 228 296 8 25 39 16 159 14 34 ] 670118796 fa\n");
+       (void) printf("[ 2 2 47 69 19 34 23 20 35 5 187 10 51 2 38 2 39 2 48 2 ] 888380310 fa\n");
+       (void) printf("[ 680 2 41 2 2 5 13 11 10 40 2 50 80 ] 1392580498 fa\n");
+       (void) printf("[ 14 25 10 7 22 49 21 22 1 4 10 23 4 13 15 5 16 15 12 3 ] 2114772893 fa\n");
+       (void) printf("[ 295 30 24 9 28 9 23 19 13 1 8 24 67 16 3 30 3 3 53 9 ] 453068702 fa\n");
+       (void) printf("[ 694 6 9 20 11 23 1 23 23 22 8 5 1 24 41 9 11 4 5 1 ] 1393470366 fa\n");
+       (void) printf("[ 944 8 ] 1770206109 fa\n");
+       (void) printf("[ 10 5 25 6 4 7 42 39 25 20 4 4 7 2 14 17 126 5 32 5 ] 113705892 fa\n");
+       (void) printf("[ 442 25 4 6 114 27 38 42 32 25 20 47 19 112 ] 998588323 fa\n");
+       (void) printf("[ 79 19 131 109 36 37 74 70 1 59 8 34 3 25 5 9 3 80 11 27 ] 1221405612 fa\n");
+       (void) printf("[ 912 9 11 ] 273962927 fa\n");
+       (void) printf("[ 8 230 25 23 6 17 130 31 61 64 16 127 32 ] 1881483187 fa\n");
+       (void) printf("[ 130 683 ] 1406620603 fa\n");
+       (void) printf("[ 18 10 32 25 5 3 10 3 143 50 13 9 61 93 86 1 1 180 48 58 ] 1980878788 fa\n");
+       (void) printf("[ 861 13 9 4 12 8 17 3 ] 1447963591 fa\n");
+       (void) printf("[ 67 143 8 128 115 435 19 2 ] 477757388 fa\n");
+       (void) printf("[ 490 35 ] 1151262673 fa\n");
+       (void) printf("[ 5 70 67 32 37 16 14 7 27 18 142 301 17 90 103 ] 1523362782 fa\n");
+       (void) printf("[ 117 14 33 38 17 13 20 26 3 453 89 3 8 113 10 ] 1908448236 fa\n");
+       (void) printf("sf cvx exec\n");
+
+       setup_user_fonts();
+#ifdef EXTCHAR
+       setup_extended_fonts();
+#endif
+       printf("%%%%EndProlog\n");
+
+       /* init for first page */
+       page1setup();
+}
+\f
+
+/* set thing up to print the first page */
+
+static void
+page1setup()
+
+{
+       Feednumber = 0;
+
+       to_next_page(Mainllhc_p);
+
+       /* Arrange to start at beginning of main list */
+       initstructs();
+
+       /* start the cursor at the top left corner of page */
+       set_cur(0.0, PGHEIGHT);
+
+       Meas_num = 1;
+       Ped_snapshot[0] = NO;
+       set_staffscale(0);
+}
+\f
+
+/* table of standard paper sizes, to be used to see if user specified
+ * a landscape version of a standard size */
+struct Papersize {
+       int     width;
+       int     height;
+} Paper_size_table[] = { 
+       { 612, 792 },   /* letter */
+       { 540, 720},    /* note */
+       { 612, 1008},   /* legal */
+       { 595, 842},    /* a4 */
+       { 421, 595},    /* a5 */
+       { 297, 421},    /* a6 */
+       { 612, 936},    /* flsa */
+       { 396, 612},    /* halfletter */
+       { 0, 0}
+};
+
+/* how many points away from an exact match to consider a match. This is big
+ * enough so that user can be off by a little and still get the desired
+ * results, yet not so big as to give false matches. */
+#ifdef FUZZ
+#undef FUZZ
+#endif
+#define FUZZ 24
+
+/* given a paper size, determine if the paper
+ * size appears to be the landscape version of a standard paper size.
+ * If so, return the page height in points, otherwise return 0.
+ * It return this rather than just a boolean
+ *  since page height is needed for translate amount.
+ */
+
+static int
+use_landscape(pgwidth, pgheight)
+
+double pgwidth;                /* page width in inches */
+double pgheight;       /* page height in inches */
+
+{
+       int pts_width, pts_height;      /* width and height in points */
+       int i;
+
+
+       /* convert dimension to points */
+       pts_width = (int) (pgwidth * PPI);
+       pts_height = (int) (pgheight * PPI);
+
+       /* for each paper size table entry, see if by interchanging the
+        * width and height we would end up with something within FUZZ
+        * points of matching a landscape mode paper size */
+       for (i = 0; Paper_size_table[i].width != 0; i++) {
+               if (pts_width > Paper_size_table[i].height - FUZZ &&
+                               pts_width < Paper_size_table[i].height + FUZZ &&
+                               pts_height > Paper_size_table[i].width - FUZZ &&
+                               pts_height < Paper_size_table[i].width + FUZZ) {
+                       return(pts_height);
+               }
+       }
+
+       /* not landscape */
+       return(0);
+}
+\f
+
+/* for any user-defined fonts, if there was any PostScript that needs to
+ * be output in order to use the font, output that.
+ */
+
+static void
+setup_user_fonts()
+
+{
+       int f;
+       char buffer[BUFSIZ];
+
+       for (f = 0; f < MAXFONTS; f++) {
+               if (Fontinfo[f].fontfile != (FILE *) 0) {
+                       while (fgets(buffer, BUFSIZ, Fontinfo[f].fontfile)
+                                               != (char *) 0) {
+                               printf("%s", buffer);
+                       }
+                       fclose(Fontinfo[f].fontfile);
+               }
+       }
+}
+\f
+
+#ifdef EXTCHAR
+
+/* for each extended character set font that was used somewhere, output
+ * the PostScript to get that font set up so that is can be used.
+ */
+
+static void
+setup_extended_fonts()
+
+{
+       int i;                  /* font index */
+       int have_extended;      /* YES if extended character set was used
+                                * somewhere, and thus we have to output
+                                * PostScript to allow using the set */
+
+
+       /* First see if there are any extended characters used at all.
+        * If not, we don't have to do anything more here */
+       have_extended = NO;
+       for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) {
+               if (Font_used[i + EXT_FONT_OFFSET] == YES) {
+                       have_extended = YES;
+                       break;
+               }
+       }
+
+       if (have_extended == NO) {
+               return;
+       }
+
+       /* first call the PostScript function to set up the encoding
+        * array for the extended character set */
+       printf("\n%% set up extended character set fonts\n");
+       printf("makeExtEncoding\n");
+
+       /* now for each extended character set font that was actually used
+        * somewhere, call the PostScript function to set up that font,
+        * based on the font from which it is derived */
+       for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) {
+               if (Font_used[i + EXT_FONT_OFFSET] == YES) {
+                       /* arguments are the name of the extended font
+                        * and the name of the base font from which it
+                        * is derived */
+                       prfontname(i + EXT_FONT_OFFSET);
+                       prfontname(i);
+                       printf("makeExtendedFont\n");
+               }
+       }
+}
+#endif
+\f
+
+/* given a LINE struct, output commands to draw a line */
+
+static void
+pr_line(line_p, fname, lineno)
+
+struct LINE *line_p;   /* info about what kind of line to draw and where */
+char *fname;           /* file name for error messages */
+int lineno;            /* line number for error messages */
+
+{
+       double x1, y1;  /* beginning of line */
+       double x2, y2;  /* end of line */
+
+       x1 = inpc_x( &(line_p->start), fname, lineno);
+       y1 = inpc_y( &(line_p->start), fname, lineno);
+       x2 = inpc_x( &(line_p->end), fname, lineno);
+       y2 = inpc_y( &(line_p->end), fname, lineno);
+
+       /* If there is a string associated with the line,
+        * print that first.
+        */
+       if (line_p->string != 0) {
+               double line_len;        /* length of line in LINE struct */
+               double str_x, str_y;    /* where string starts */
+
+               /* First find length of line. */
+               line_len = sqrt(SQUARED(x2 - x1) + SQUARED(y2 - y1));
+               if (x2 < x1) {
+                       line_len = -line_len;
+               }
+
+               /* For now, pretend the line is horizontal, starting
+                * at (x1,y1). The horizontal middle of the string should then
+                * be at the midpoint of the line, and the left edge of the
+                * string should be half the string width left of that.
+                * The vertical is a STEPSIZE above the line.
+                */
+               str_x = (line_len / 2.0) - (strwidth(line_p->string) / 2.0);
+               str_y = STEPSIZE + strdescent(line_p->string);
+
+               /* move effective origin of coordinate system to (x1,y1),
+                * then rotate by the appropriate angle and print string.
+                */
+               outop(O_GSAVE);
+               outcoord(x1);
+               outcoord(y1);
+               outop(O_TRANSLATE);
+               /* calculate angle. If vertical line or nearly so,
+                * avoid division by zero */
+               if (fabs(x2 - x1) < .001) {
+                       outint(90);
+               }
+               else {
+                       OUTP(("%.1f ", atan( (y2 - y1) / (x2 - x1) ) * 180.0 / PI));
+               }
+               
+               outop(O_ROTATE);
+               pr_string(str_x, str_y, line_p->string, J_LEFT, 0, -1);
+               outop(O_GRESTORE);
+       }
+
+       /* wavy lines are special case */
+       if (line_p->linetype == L_WAVY) {
+               draw_wavy(x1, y1, x2, y2);
+               return;
+       }
+
+       /* set line width to specified width and type, then draw the line */
+       do_linetype(line_p->linetype);
+       draw_line (x1, y1, x2, y2);
+
+       /* make sure line type gets set back to solid */
+       if (line_p->linetype == L_DASHED || line_p->linetype == L_DOTTED) {
+               do_linetype(L_NORMAL);
+       }
+}
+\f
+
+/* generate PostScript command to tell what kind of line to draw */
+
+void
+do_linetype(ltype)
+
+int ltype;             /* L_WIDE, L_NORMAL, etc  */
+
+{
+       if (Last_linetype == ltype && Last_staffscale == Staffscale) {
+               /* same as last time, no need to tell the printer again */
+               return;
+       }
+
+       /* output command for proper width/type of line */
+       switch(ltype) {
+
+       case L_WIDE:
+               OUTP(("%4.2f ", W_WIDE * Staffscale));
+               outop(O_LINEWIDTH);
+               break;
+
+       case L_NORMAL:
+               OUTP(("%4.2f ", W_NORMAL * Staffscale));
+               outop(O_LINEWIDTH);
+               break;
+
+       case L_MEDIUM:
+               OUTP(("%4.2f ", W_MEDIUM * Staffscale));
+               outop(O_LINEWIDTH);
+               break;
+
+       case L_DOTTED:
+               OUTP(("%4.2f ", Staffscale));
+               outop(O_LINEWIDTH);
+               outop(O_DOTTED);
+               Doing_dotted = YES;
+               break;
+
+       case L_DASHED:
+               OUTP(("%4.2f ", Staffscale));
+               outop(O_LINEWIDTH);
+               outop(O_DASHED);
+               Doing_dotted = YES;
+               break;
+
+       default:
+               pfatal("unknown line type");
+               break;
+       }
+
+       /* remember current line type */
+       Last_linetype = ltype;
+       Last_staffscale = Staffscale;
+
+       /* if was doing dotting but not anymore, tell PostScript */
+       if (Doing_dotted && (ltype != L_DOTTED) && (ltype != L_DASHED)) {
+               Doing_dotted = NO;
+               outop(O_ENDDOTTED);
+       }
+}
+\f
+
+/* output commands for drawing a line. Resulting output is:
+ *     x1 y1 moveto x2 y2 lineto stroke */
+
+void
+draw_line(x1, y1, x2, y2)
+
+double x1, y1; /* draw line from here */
+double x2, y2; /* to here */
+
+{
+       dr_line( (double) x1, (double) y1, (double) x2, (double) y2, O_LINE);
+}
+
+
+/* output commands to draw a line whose width is proportional to the given
+ * point size */
+
+void
+draw_prop_line(x1, y1, x2, y2, size, ltype)
+
+double x1, y1; /* draw line from here */
+double x2, y2; /* to here */
+int size;      /* make width proportional to this */
+int ltype;     /* O_LINE, etc */
+
+{
+       /* temporarily change the line width, then draw the line */
+       outop(O_GSAVE);
+       OUTP(("%.2f ", (double) size * 0.065 * Staffscale));
+       outop(O_LINEWIDTH);
+       dr_line(x1, y1, x2, y2, O_LINE);
+       outop(O_GRESTORE);
+}
+\f
+
+/* draw a wavy line. Resulting output is:
+ *     x1 y1 moveto x2 y2 wavy */
+
+void
+draw_wavy(x1, y1, x2, y2)
+
+double x1;
+double y1;     /* draw wavy line from x1,y1 */
+double x2;
+double y2;     /* to x2, y2 */
+
+{
+       dr_line((double) x1, (double) y1, (double) x2, (double) y2, O_WAVY);
+}
+\f
+
+/* actually draw line. Common function for drawing regular or wavy lines */
+
+static void
+dr_line(x1, y1, x2, y2, ltype)
+
+double x1;
+double y1;     /* draw line from x1,y1 */
+double x2;
+double y2;     /* to x2,y2 */
+int ltype;     /* O_LINE, etc */
+
+{
+
+       /* output coordinates */
+       outcoord(x1);
+       outcoord(y1);
+       outop(O_MOVETO);
+
+       outcoord(x2);
+       outcoord(y2);
+       outop(ltype);
+
+       /* current location is where line ended */
+       set_cur(x2, y2);
+}
+\f
+
+/* output commands to draw a curve */
+
+static void
+pr_curve(curve_p, fname, lineno)
+
+struct CURVE *curve_p; /* which curve */
+char *fname;           /* file name for error messages */
+int lineno;            /* line number for error messages */
+
+{
+       struct INPCOORD *inpcoord_p;
+       int n;
+       float *xlist, *ylist;
+       float cwid;             /* curve width */
+       int taper;              /* YES or NO */
+
+
+       /* pr_allcurve() expects lists of X and Y coordinates, so
+        * get some space for those lists,  and fill them in.
+        * Call pr_allcurve() to print the curve, then free the lists */
+       MALLOCA(float, xlist, curve_p->ncoord);
+       MALLOCA(float, ylist, curve_p->ncoord);
+       for (n = 0; n < curve_p->ncoord; n++) {
+               inpcoord_p = &(curve_p->coordlist[n]);
+               xlist[n] = inpc_x(inpcoord_p, fname, lineno);
+               ylist[n] = inpc_y(inpcoord_p, fname, lineno);
+       }
+       switch(curve_p->curvetype) {
+       case L_NORMAL:
+               cwid = W_NORMAL / PPI;
+               taper = YES;
+               break;
+       case L_MEDIUM:
+               cwid = W_MEDIUM / PPI;
+               taper = YES;
+               break;
+       case L_WIDE:
+               cwid = W_WIDE / PPI;
+               taper = YES;
+               break;
+       case L_DASHED:
+               cwid = 1.0 / PPI;
+               taper = NO;
+               do_linetype(L_DASHED);
+               break;
+       case L_DOTTED:
+               cwid = 1.0 / PPI;
+               taper = NO;
+               do_linetype(L_DOTTED);
+               break;
+       default:
+               pfatal("unknown curve type");
+               /*NOTREACHED*/
+               return;
+       }
+       /* adjust for current staff scaling */
+       cwid *= Staffscale;
+
+       pr_allcurve(xlist, ylist, curve_p->ncoord, cwid, taper);
+       FREE(xlist);
+       FREE(ylist);
+       /* make sure line type gets set back to solid */
+       if (curve_p->curvetype == L_DASHED || curve_p->curvetype == L_DOTTED) {
+               do_linetype(L_NORMAL);
+       }
+}
+\f
+
+/* functions to do common PostScript things */
+
+void
+do_moveto(x, y)
+
+double x;
+double y;
+
+{
+       outcoord(x);
+       outcoord(y);
+       outop(O_MOVETO);
+}
+
+
+void
+do_line(x, y)
+
+double x;
+double y;
+
+{
+       outcoord(x);
+       outcoord(y);
+       outop(O_LINETO);
+}
+
+void
+do_curveto(x1, y1, x2, y2, x3, y3)
+
+double x1, y1;
+double x2, y2;
+double x3, y3;
+
+{
+       outcoord(x1);
+       outcoord(y1);
+       outcoord(x2);
+       outcoord(y2);
+       outcoord(x3);
+       outcoord(y3);
+       outop(O_CURVETO);
+}
+
+
+void
+do_stroke()
+
+{
+       outop(O_STROKE);
+}
+
+void
+do_fill()
+
+{
+       outop(O_FILL);
+}
+
+void
+do_newpath()
+
+{
+       outop(O_NEWPATH);
+}
+
+void
+do_closepath()
+
+{
+       outop(O_CLOSEPATH);
+}
+
+/* output a PostScript scale command */
+
+static void
+do_scale(xscale, yscale)
+
+double xscale, yscale;
+
+{
+       OUTP(("%0.6f %0.6f ", xscale, yscale));
+       outop(O_SCALE);
+}
+
+/* print a white box with the corners given */
+
+void
+do_whitebox(x1, y1, x2, y2)
+
+double x1, y1;
+double x2, y2;
+
+{
+       outcoord(x1);
+       outcoord(y1);
+       outcoord(x2);
+       outcoord(y2);
+       OUTP(("whitebox\n"));
+}
+\f
+
+/* output PostScript to draw a guitar grid */
+
+void
+do_grid(x, y, space, grid_p, staff)
+
+double x;
+double y;
+double space;  /* distance between lines of the grid */
+struct GRID *grid_p;
+int staff;
+
+{
+       int s;
+       int fret, fretnum, numvert;
+       int topfret;
+
+       outcoord(x);
+       outcoord(y);
+       outcoord(space);
+
+       gridinfo(grid_p, staff, &fret, &fretnum, &numvert, &topfret);
+       outint(fret);
+       outint(fretnum);
+       outint(numvert);
+
+       /* the curve ends */
+       outint(grid_p->curvel);
+       outint(grid_p->curver);
+
+       /* fret value for each string in a PostScript array */
+       OUTP(("[ "));
+       for (s = 0; s < grid_p->numstr; s++) {
+               if (grid_p->positions[s] <= 0) {
+                       outint(grid_p->positions[s]);
+               }
+               else {
+                       outint(grid_p->positions[s] - topfret);
+               }
+       }
+       OUTP(("] grid\n"));
+}
+\f
+
+/* output commands for printing one music character */
+
+void
+pr_muschar(x, y, ch, size, font)
+
+float x, y;    /* where to print */
+int ch;                /* which music character to print */
+int size;
+int font;      /* FONT_MUSIC*   */
+
+{
+       outp_muschar(x, y, ch, size, font);
+
+       /* x of music char is in middle, so set current to right edge */
+       size = adj_size(size, Staffscale, (char *) 0, -1);
+       set_cur(x + width(font, size, ch) / 2.0, y);
+}
+\f
+
+/* Output PostScript to print a music character. Common part for
+ * normal and italic versions of the character. */
+
+static void
+outp_muschar(x, y, ch, size, font)
+
+double x, y;
+int ch;
+int size;
+
+{
+       double scaling;
+
+       /* tell where to print it */
+       outcoord( (double) x);
+       outcoord( (double) y);
+
+       if (size == DFLT_SIZE) {
+               scaling = Staffscale;
+       }
+       else {
+               scaling = (double) size * Staffscale / (double) DFLT_SIZE;
+       }
+       OUTP(("%f ", scaling));
+
+       /* output the symbolic name of the music character */
+       OUTP(("%s\n", mc_num2name(ch, font)));
+}
+\f
+
+/* Print an italic music character. We do this by constructing an
+ * appropriate PostScript transform matrix and then printing the character.
+ * The transform matrix takes the rectangle that bounds the character,
+ * widens it slightly, and and turns it into a parallelogram
+ * slanted by 15 degrees.
+ *    ----------          ---------
+ *    |        |  -->    /       /
+ *    |        |        /       /
+ *    ----------        --------
+ */
+
+static void
+pr_ital_muschar(x, y, ch, size, font)
+
+double x, y;   /* where to print */
+int ch;                /* which music character to print */
+int size;
+int font;      /* MUSIC_FONT*  */
+
+{
+       float chwidth, chheight;
+       float adj;              /* distance top left is moved to get slant */
+       float inc;              /* increment on width */
+       float a, c;             /* for transform equation  x' = ax + cy + t  */
+       int eff_size;
+
+
+       eff_size = adj_size(size, Staffscale, (char *) 0, -1);
+       chheight = height(font, eff_size, ch);
+       chwidth = width(font, eff_size, ch);
+       /* Widen some so doesn't look so cramped. This may 
+        * encroach on neighboring characters, but if they are italic
+        * too--which they probably are--they probably slant enough
+        * to stay out of the way. */
+       inc = MIN(chwidth, chheight * 0.8) / 4.0;
+
+       /* we want to slant by 15 degrees, so use tangent of 15 degrees */
+       adj = chheight * 0.27;
+       /* if character is really narrow, don't slant so much--
+        * don't squeeze character to less than half its original width */
+       if (adj > chwidth / 2.0) {
+               adj = chwidth / 2.0;
+       }
+       
+       /* Temporarily change the transform matrix.
+        * The y value is unchanged by the transform.
+        * The new x is
+        *      x' = ax + cy + t
+        * where t is 0, and a and c are as stated below.
+        */
+       a = (chwidth + 2 * inc - adj) / chwidth;
+       c = adj / chheight;
+
+       outop(O_GSAVE);
+       OUTP(("[ %f 0.0 %f 1.0 0.0 0.0 ] ", a, c));
+       outop(O_CONCAT);
+
+       /* The x location will get adjusted by the new transform matrix,
+        * so we have to compensate so it will appear where it should.
+        * We take the PostScript transform matrix equation given above,
+        * then set x' to the x value that was passed in to us,
+        * and rearrange to solve for x.
+        */
+       outp_muschar((x - c * y) / a, y, ch, size, font);
+
+       /* return to previous transform matrix */
+       outop(O_GRESTORE);
+
+       /* x of music char is in middle, so set current to right edge */
+       set_cur(x + width(font, eff_size, ch) / 2.0, y);
+}
+\f
+
+/* print bar line */
+
+static void
+pr_bar(mll_p, x, is_pseudobar)
+
+struct MAINLL *mll_p;  /* print bar connected here */
+double x;              /* x coordinate */
+int is_pseudobar;      /* YES if is pseudobar at beginning of score */
+
+{
+       register int s;         /* staff number */
+       register int n;         /* index into range list */
+       struct BAR *bar_p;      /* info about the bar */
+       struct MAINLL *m_p;     /* to walk through main list */
+       int next_is_restart = NO;       /* if following bar is a restart */
+
+
+       debug(512, "pr_bar");
+
+       if (is_pseudobar == YES) {
+               bar_p = mll_p->u.clefsig_p->bar_p;
+       }
+       else {
+               bar_p = mll_p->u.bar_p;
+       }
+
+       /* We need to know if the following bar (if any) is a restart,
+        * because then this one will have to be handled like it is at
+        * the right margin, so find out. */
+       for (m_p = mll_p->next; m_p != (struct MAINLL *) 0; m_p = m_p->next) {
+               if (m_p->str == S_FEED) {
+                       /* If there was a restart, it's been moved to this
+                        * feed and is thus now irrelevant. */
+                       break;
+               }
+               /* If there is a clefsig, then even if there is a restart
+                * we should not remove this bar's right padding--
+                * there is still some staff after it for the
+                * clef/keysig/time (whatever subset is specified by clefsig),
+                * and moving the bar would cause them to get too close. */
+               if (m_p->str == S_CLEFSIG) {
+                       break;
+               }
+               if (m_p->str == S_BAR) {
+                       if (m_p->u.bar_p->bartype == RESTART) {
+                               next_is_restart = YES;
+                       }
+                       /* we've looked ahead far enough */
+                       break;
+               }
+       }
+
+       /* go down the bar list and the list of staffs */
+       for (s = 1, n = 0; n < Score.nbarst; n++) {
+
+               /* everything up to next range is barred individually */
+               for (   ; s < Score.barstlist[n].top; s++) {
+                       pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p);
+               }
+
+               /* everything in the range is barred together */
+               pr_bar_range(bar_p, Score.barstlist[n].top,
+                       Score.barstlist[n].bottom, x, next_is_restart, mll_p);
+               s = Score.barstlist[n].bottom + 1;
+       }
+
+       /* any remaining are barred individually */
+       for (   ; s <= Score.staffs; s++) {
+               pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p);
+       }
+
+       /* If user specified a measure number use that */
+       if (bar_p->mnum > 0) {
+               Meas_num = bar_p->mnum;
+       }
+       /* if basictime of the last STAFF we saw was < -1, then
+        * it was a multirest, so the measure number needs to
+        * be incremented by the number of measures of multirest.
+        * Since this is stored as a negative, we subtract the
+        * negative to get the same effect as adding the absolute
+        * value */
+       else if ( (Last_staff != (struct STAFF *) 0)
+                       && (is_pseudobar == NO)
+                       && (Last_staff->groups_p[0] != (struct GRPSYL *) 0)
+                       && (Last_staff->groups_p[0]->basictime < -1) ) {
+               Meas_num -= Last_staff->groups_p[0]->basictime;
+       }
+       else if ((bar_p->bartype != INVISBAR) && (bar_p->bartype != RESTART)
+                                               && (is_pseudobar == NO)) {
+               /* normal case, not multirest; just increment measure number */
+               Meas_num++;
+       }
+
+       /* print rehearsal mark if any */
+       if (is_pseudobar == NO) {
+               pr_reh(mll_p);
+       }
+
+       /* take care of pedal marks for the measure */
+       if (is_pseudobar == NO) {
+               pr_ped_bar(mll_p, bar_p);
+       }
+}
+\f
+
+/* given a range of staffs to bar together, find which are visible and from
+ * that, the y-coordinates of the ends of the bar line, and draw it */
+
+static void
+pr_bar_range(bar_p, topstaff, botstaff, x, next_is_restart, mll_p)
+
+struct BAR *bar_p;     /* info about bar */
+int topstaff;          /* top staff to be barred together */
+int botstaff;          /* bottom staff to be barred together */
+double x;              /* x coordinate of where to draw the bar */
+int next_is_restart;   /* YES if following bar is RESTART */
+struct MAINLL *mll_p;  /* to get effective margin */
+
+{
+       float y1, y2;           /* top and bottom of bar line */
+       float halfbarwidth;     /* half the width of the bar line */
+       int staffno;
+       int stafflines;
+
+
+       /* check for null pointer to avoid core dumps */
+       if (Score_location_p == (float *) 0) {
+               pfatal("can't print bar: no feed");
+               return;
+       }
+
+       /* Normally, we want some padding on both sides of a bar line,
+        * but at the end of a staff, we don't want right padding.
+        * This applies either if we are at the right
+        * margin or if the next bar is a restart. */
+       halfbarwidth = width_barline(bar_p) / 2.0;
+       /* Make sure bars line at end of score are precisely at the end */
+       if (PGWIDTH - eff_rightmargin(mll_p) - x <= halfbarwidth + 3.0 * STDPAD) {
+               /* Should only hit this now if there is a bug in placement
+                * of last bar line in a score, but since we changed how
+                * that is determined, better safe than sorry. */
+               x = PGWIDTH - eff_rightmargin(mll_p) - halfbarwidth
+                                                       + eos_bar_adjust(bar_p);
+       }
+
+       /* Similarly, make sure bars line just before a restart
+        * are precisely at the point where the restart whitebox starts. */
+       if (next_is_restart) {
+               struct MAINLL *m_p;
+
+               /* find the restart */
+               for (m_p = mll_p; m_p != 0; m_p = m_p->next) {
+                       if (m_p->str == S_BAR && m_p->u.bar_p->bartype == RESTART) {
+                               if (m_p->u.bar_p->c[AX] - HALF_RESTART_WIDTH
+                                               - m_p->u.bar_p->padding - x
+                                               <= halfbarwidth + 2.0 * STDPAD) {
+                                       x = m_p->u.bar_p->c[AX]
+                                                       - HALF_RESTART_WIDTH
+                                                       - m_p->u.bar_p->padding
+                                                       - halfbarwidth
+                                                       + eos_bar_adjust(bar_p);
+                               }
+                               break;
+                       }
+               }
+       }
+
+       /* go through the range of staffs */
+       /* Note: y2 doesn't really need to be set here, it's just to shut up
+        * compilers that think it could be used without being set. */
+       for (y1 = y2 = -1.0, staffno = topstaff; staffno <= botstaff; staffno++) {
+
+               /* only worry about visible staffs */
+               if ( (svpath(staffno, VISIBLE))->visible == YES) {
+
+                       stafflines = svpath(staffno, STAFFLINES)->stafflines;
+                       set_staffscale(staffno);
+
+                       /* if hadn't found any staff yet to bar, now we have */
+                       if (y1 < 0.0) {
+                               if (stafflines < 2) {
+                                       y1 = Staffs_y[staffno] +
+                                                       (2.0 * Stepsize);
+                               }
+                               else {
+                                       y1 = Staffs_y[staffno] +
+                                               (stafflines - 1) * Stepsize
+                                               * (is_tab_staff(staffno) ?
+                                               TABRATIO : 1.0);
+                                       /* 2-line staffs get a bit more, so
+                                        * repeat sign dots have something
+                                        * to be next to */
+                                       if (stafflines == 2) {
+                                               y1 += 2 * Stepsize;
+                                       }
+                               }
+                       }
+
+                       /* this is the bottom one found so far */
+                       if (stafflines < 2) {
+                               y2 = Staffs_y[staffno] - (2.0 * Stepsize);
+                       }
+                       else {
+                               y2 = Staffs_y[staffno] -
+                                       (stafflines - 1) * Stepsize *
+                                       (is_tab_staff(staffno) ? TABRATIO : 1.0);
+                               if (stafflines == 2) {
+                                       y2 -= 2 * Stepsize;
+                               }
+                       }
+
+                       /* if repeat, print the dots */
+                       pr_repeat_dots(bar_p->bartype, staffno, (double) x);
+               }
+       }
+
+       /* if any were visible, we draw the bar line now */
+       if (y1 > 0.0) {
+               draw_bar(bar_p->bartype, bar_p->linetype,
+                                       (double) x, (double) y1, (double) y2);
+       }
+}
+\f
+
+/* actually draw a bar line of the proper type at specified place */
+
+/*--- Note: any changes in width made here have to be reflected in
+ * pr_bar_range() for adjustment when at right edge of page, and
+ * in width_barline() */
+
+static void
+draw_bar(bartype, linetype, x, y1, y2)
+
+int bartype;   /* info about single, double, repeat, etc */
+int linetype;
+double x;
+double y1;     /* top of bar line */
+double y2;     /* bottom of bar line */
+
+{
+       /* always use default staffscale for bar lines since they are
+        * not associated with any particular staff */
+       set_staffscale(0);
+       do_linetype(linetype);
+       /* dashed/dotted look better if we offset them slightly */
+       if (linetype == L_DASHED || linetype == L_DOTTED) {
+               y1 -= Stepsize * 0.375;
+               y2 += Stepsize * 0.1;
+       }
+
+       switch (bartype) {
+
+       case DOUBLEBAR:
+               draw_line(x - 2.0 * STDPAD, y1, x - 2.0 * STDPAD, y2);
+               draw_line(x + STDPAD, y1, x + STDPAD, y2);
+               break;
+
+       case SINGLEBAR:
+               draw_line(x, y1, x, y2);
+               break;
+
+       case REPEATSTART:
+               draw_line(x + STDPAD, y1, x + STDPAD, y2);
+               do_linetype(L_WIDE);
+               draw_line(x - (3.0 * STDPAD), y1, x - (3.0 * STDPAD), y2);
+               break;
+
+       case REPEATEND:
+               draw_line(x, y1, x, y2);
+               do_linetype(L_WIDE);
+               draw_line(x + (4.0 * STDPAD), y1, x + (4.0 * STDPAD), y2 );
+               break;
+
+       case REPEATBOTH:
+               do_linetype(L_WIDE);
+               draw_line(x - (2.5 * STDPAD), y1, x - (2.5 * STDPAD), y2);
+               draw_line(x + (2.5 * STDPAD), y1, x + (2.5 * STDPAD), y2);
+               break;
+
+       case ENDBAR:
+               draw_line(x - (2.0 * STDPAD), y1, x - (2.0 * STDPAD), y2);
+               do_linetype(L_WIDE);
+               draw_line(x + (2.0 * STDPAD), y1, x + (2.0 * STDPAD), y2);
+               break;
+
+       case RESTART:
+               /* This is a "funny" bar that is drawn when the staff lines
+                * are printed, so there isn't anything to be done here. */
+               break;
+
+       case INVISBAR:
+               /* nothing to do! */
+               break;
+
+       default:
+               pfatal("bad bar type");
+       }
+       do_linetype(L_NORMAL);
+}
+\f
+
+/* print the dots for a repeat sign */
+
+static void
+pr_repeat_dots(bartype, staff, x)
+
+int bartype;   /* repeatstart, repeatend, repeatboth, etc */
+int staff;     /* which staff to print on */
+double x;      /* horizontal position */
+
+{
+       float y;        /* vertical position of middle of staff */
+       double topoffset, bottomoffset;         /* dot offset */
+       double adjust;          /* adjustment for tablature and/or staffscale */
+       int stafflines;
+       
+
+       /* If no dots, don't bother */
+       if (bartype != REPEATSTART && bartype != REPEATEND
+                                               && bartype != REPEATBOTH) {
+               return;
+       }
+
+       if (Score_location_p == (float *) 0) {
+               /* this should never be hit--we already checked earlier */
+               pfatal("can't do repeat: no feed");
+               return;
+       }
+
+       /* get y offset based on staff */
+       y = Staffs_y[staff];
+       adjust = Stepsize * (is_tab_staff(staff) ? TABRATIO : 1.0);
+       bottomoffset = topoffset = adjust;
+
+       /* if even number of staff lines, compensate by moving up */
+       stafflines = svpath(staff, STAFFLINES)->stafflines;
+       if ( (stafflines & 1) == 0) {
+               y += adjust;
+       }
+
+       /* if more than 5 lines on staff, leave one blank space between
+        * the dots */
+       if (stafflines > 5) {
+               if ( (stafflines & 1) == 0) {
+                       /* even number of staff lines, move bottom down */
+                       bottomoffset = 3 * adjust;
+               }
+               else {
+                       /* odd number of lines, move top up */
+                       topoffset = 3 * adjust;
+               }
+       }
+       
+
+       /* print dots at appropriate locations */
+       switch(bartype) {
+
+       case REPEATSTART:
+               do_rdots((double) (x + (4.0 * STDPAD)), (double) y, topoffset,
+                                       bottomoffset);
+               break;
+
+       case REPEATBOTH:
+               do_rdots((double) (x + (7.0 * STDPAD)), (double) y, topoffset,
+                                       bottomoffset);
+               do_rdots((double) (x - (7.0 * STDPAD)), (double) y, topoffset,
+                                       bottomoffset);
+               break;
+
+       case REPEATEND:
+               do_rdots((double) (x - (4.0 * STDPAD)), (double) y, topoffset,
+                                       bottomoffset);
+               break;
+
+       default:
+               /* other types of bars don't have dots */
+               break;
+       }
+}
+\f
+
+/* print the 2 dots for a repeat sign */
+
+static void
+do_rdots(x, y, topoffset, bottomoffset)
+
+double x;
+double y;      /* y is a middle of staff, so offset from there */
+double topoffset, bottomoffset;                /* offset from y in each direction */
+
+{
+       pr_muschar(x, y + topoffset, C_DOT, DFLT_SIZE, FONT_MUSIC);
+       pr_muschar(x, y - bottomoffset, C_DOT, DFLT_SIZE, FONT_MUSIC);
+}
+\f
+
+/* print any rehearsal marks associated with bar line */
+
+static void
+pr_reh(mll_p)
+
+struct MAINLL *mll_p;  /* current bar line is off of here */
+
+{
+       struct MARKCOORD *mark_p;       /* where to put rehearsal mark */
+       float y;                        /* vertical location */
+       struct BAR *bar_p;
+       char *str;                      /* the string, with box or circle
+                                        * or nothing as appropriate for
+                                        * the associated staff */
+
+
+       if (mll_p->str == S_BAR) {
+               bar_p = mll_p->u.bar_p;
+       }
+       else {
+               bar_p = mll_p->u.clefsig_p->bar_p;
+       }
+
+       for (mark_p = bar_p->reh_p; mark_p != (struct MARKCOORD *) 0;
+                                       mark_p = mark_p->next) {
+
+               /* print rehearsal mark if any */
+               if (bar_p->reh_string != (char *) 0) {
+
+                       y = Staffs_y[mark_p->staffno] + mark_p->ry;
+
+                       /* get boxed or circled version if appropriate */
+                       str = get_reh_string(bar_p->reh_string, mark_p->staffno);
+                       pr_string((double) bar_p->c[AX] - left_width(str),
+                                       (double) y, str, J_LEFT,
+                                       mll_p->inputfile, mll_p->inputlineno);
+               }
+       }
+}
+\f
+
+/* draw a box of given size at given x,y */
+
+static void
+pr_box(x, y, boxheight, boxwidth)
+
+double x, y;
+double boxheight, boxwidth;
+
+{
+       do_linetype(L_NORMAL);
+       do_newpath();
+       do_moveto(x, y);
+       do_line(x, y + boxheight);
+       do_line(x + boxwidth, y + boxheight);
+       do_line(x + boxwidth, y);
+       do_closepath();
+       do_stroke();
+}
+\f
+
+/* do a feed (newscore and maybe newpage) */
+
+void
+pr_feed(main_feed_p)
+
+struct MAINLL *main_feed_p;    /* MAINLL struct pointing to FEED */
+
+{
+       register int s;         /* walk through staffs */
+       float lowest_y = 0.0;   /* y coord of bottom staff. Initialization is
+                                * solely to shut up bogus compiler warning */
+       float highest_y = 0.0;
+       int printed;            /* How many staffs printed so far */
+       int had_br_br;          /* YES if had braces and/or brackets printed */
+       int need_vert_line = NO;        /* if need line at left edge */
+       struct FEED *feed_p;
+       int stafflines;
+       double y;
+
+
+       debug(256, "pr_feed lineno=%d", main_feed_p->inputlineno);
+
+       feed_p = main_feed_p->u.feed_p;
+
+       /* If user put top/bottom or newpage at the very end of the file,
+        * we could end up with a page with nothing but header/footer.
+        * So if there is no good reason to do another page, we don't. */
+       if (Atend_info.separate_page == NO && main_feed_p->next == 0) {
+               /* Nothing at all after the feed,
+                * so no need to make another page. */
+               return;
+       }
+
+       /* if doing a page feed, print the headers and footers on the
+        * current page and move on to the next one */
+       if (feed_p->pagefeed == YES) {
+               newpage(main_feed_p);
+       }
+
+       /* If there is a top and/or bot block, print those.
+        * Even though from user's viewpoint the current page may
+        * use top2/bot2, placement phase will have set top_p/bot_p
+        * to whatever is appropriate for this page.
+        */
+       if (feed_p->top_p != 0) {
+               y = PGHEIGHT - EFF_TOPMARGIN
+                       - (Feednumber == 1 ? Header.height : Header2.height);
+               pr_topbot(feed_p->top_p, y);
+       }
+       if (feed_p->bot_p != 0) {
+               y = EFF_BOTMARGIN + feed_p->bot_p->height
+                       + (Feednumber == 1 ? Footer.height : Footer2.height);
+               pr_topbot(feed_p->bot_p, y);
+       }
+
+       if (main_feed_p->next == 0) {
+               /* Feed at end of piece, presumably to force
+                * gridsatend onto separate page or something like that */
+               return;
+       }
+       if (main_feed_p->next->str != S_CLEFSIG) {
+               /* Must be BLOCKHEAD or lines/curves at end of file.
+                * In any case, no actual music staffs to print. */
+               return;
+       }
+
+       /* now do score feed stuff */
+       /* keep track of where the staffs are: we need this for
+        * drawing lots of other things relative to the staffs */
+       Score_location_p = feed_p->c;
+       set_staff_y(main_feed_p);
+
+       if (Feednumber == 1 && Meas_num == 1) {
+               /* first time through. See if the song begins with a
+                * "pickup" measure, i.e., its first chord is all spaces.
+                * If so, don't count that measure in measure number. */
+               if (has_pickup() == YES) {
+                       Meas_num--;
+               }
+       }
+
+       /* for each staff */
+       for ( printed = 0, s = 1; s <= Score.staffs; s++) {
+
+               /* print if visible */
+               if ( (svpath(s, VISIBLE))->visible == YES) {
+
+                       stafflines = svpath(s, STAFFLINES)->stafflines;
+                       set_staffscale(s);
+                       if (stafflines < 3) {
+                               lowest_y = Staffs_y[s] - (2.0 * Stepsize)
+                                       * (is_tab_staff(s) ? TABRATIO : 1.0);
+                       }
+                       else {
+                               lowest_y = Staffs_y[s] - (stafflines - 1)
+                                       * Stepsize * (is_tab_staff(s) ?
+                                       TABRATIO : 1.0);
+                       }
+
+                       /* find the top of the score */
+                       if (printed == 0) {
+                               if (stafflines < 3) {
+                                       highest_y = Staffs_y[s]
+                                       + (2.0 * Stepsize)
+                                       * (is_tab_staff(s) ? TABRATIO : 1.0);
+                               }
+                               else {
+                                       highest_y = Staffs_y[s]
+                                       + (stafflines - 1)
+                                       * Stepsize * (is_tab_staff(s) ?
+                                       TABRATIO : 1.0);
+                               }
+                       }
+
+                       printed++;
+
+                       outcoord( (double) (Score_location_p[AX] + x1a));
+                       outcoord( (double) (Staffs_y[s] + ya));
+                       outcoord( (double) (Score_location_p[AE] + x2a));
+                       OUTP(("%d %f %f ", svpath(s, STAFFLINES)->stafflines,
+                               (is_tab_staff(s) == YES ? TABRATIO : 1.0),
+                               Staffscale));
+                       outop(O_STAFF);
+
+                       /* print measure number at beginning of staff if
+                        * necessary */
+                       pr_meas_num(s, Score_location_p[AX]);
+               }
+       }
+
+       /* print brace/bracket and group label */
+       had_br_br = pr_brac(NO, 0.0, main_feed_p);
+
+       if (printed == 0) {
+               /* we check for this earlier, so should never hit this */
+               pfatal("no staffs visible");
+       }
+
+       /* draw vertical line at left edge of staffs */
+       /* but don't draw if only one staff and no brace/bracket */
+       if ((printed > 1) || (had_br_br != NO)) {
+               need_vert_line = YES;
+               do_linetype(L_NORMAL);
+               draw_line(Score_location_p[AX], highest_y,
+                                       Score_location_p[AX],  lowest_y);
+       }
+
+       pr_restarts(main_feed_p, highest_y, lowest_y, need_vert_line);
+
+       /* set current to x,y of score */
+       set_cur(Score_location_p[AX], Score_location_p[AY]);
+}
+\f
+
+/* Given a BLOCKHEAD for a top/bottom and a y location, print the
+ * contents of the BLOCKHEAD at that location.
+ */
+
+static
+void pr_topbot(blockhead_p, y)
+
+struct BLOCKHEAD *blockhead_p;
+double y;
+
+{
+       double x;
+
+       x = eff_leftmargin(0);
+       /* Set up window coordinates, go to upper left of window, and print */
+       set_win_coord(blockhead_p->c);
+       set_win(y, y - blockhead_p->height, PGWIDTH - eff_rightmargin(0), x);
+       set_cur(x, y);
+       pr_print(blockhead_p->printdata_p);
+       set_win_coord(0);
+}
+\f
+
+/* We want to print all the "restart" bars right after the staff lines,
+ * so in case anything spills into the white space we write over the staffs,
+ * it won't get obliterated. So find any restarts till the next feed and
+ * put out a whitebox and do and brace/backets and vertical line needed.
+ */
+
+static void
+pr_restarts(mll_p, y1, y2, need_vert_line)
+
+struct MAINLL *mll_p;
+double y1;
+double y2;
+int need_vert_line;
+
+{
+       double x;
+
+       for (mll_p = mll_p->next; mll_p != (struct MAINLL *) 0;
+                                               mll_p = mll_p->next) {
+               if (mll_p->str == S_FEED) {
+                       /* we went far enough */
+                       return;
+               }
+
+               if (mll_p->str == S_BAR && mll_p->u.bar_p->bartype == RESTART) {
+                       x = mll_p->u.bar_p->c[AX];
+                       /* Expand the y dimensions to make sure we completely
+                        * erase the top and bottom staff lines. */
+                       do_whitebox(x - HALF_RESTART_WIDTH
+                                       - mll_p->u.bar_p->padding,
+                                       y1 + POINT,
+                                       x + HALF_RESTART_WIDTH, y2 - POINT);
+
+                       /* print braces/brackets */
+                       pr_brac(YES, x + POINT, mll_p);
+
+                       /* draw vertical line, if needed */
+                       x += HALF_RESTART_WIDTH - (W_NORMAL / PPI) / 2.0;
+                       if (need_vert_line == YES) {
+                               do_linetype(L_NORMAL);
+                               draw_line(x, y1, x,  y2);
+                       }
+
+               }
+       }
+}
+\f
+
+/* print a brace or bracket */
+
+void
+do_pr_brac(x, y1, y2, which)
+
+double x;      /* coordinates at which to draw brace or bracket */
+double y1;
+double y2;
+int which;     /* BRACELIST or BRACKLIST */
+
+{
+       outcoord(x);
+       outcoord(y1);
+       outcoord(y2);
+       outop( which == BRACELIST ? O_BRACE : O_BRACKET);
+}
+\f
+
+/* output a coordinate. Convert from inches to points, to 0.01 point accuracy */
+
+void
+outcoord(val)
+
+double val;            /* an x or y value */
+
+{
+       OUTP(("%.2f ", val * PPI));
+}
+\f
+
+
+/* output an integer value */
+
+static void
+outint(val)
+
+int val;
+
+{
+       OUTP(("%d ", val));
+}
+\f
+
+/* output a string to be printed. Have to walk through the string
+ * one character at a time, possibly breaking into several strings
+ * if there are font/size changes or music characters in the middle */
+
+static void
+outstring(x, y, fullwidth, string, fname, lineno)
+
+double x;      /* where to print string */
+double y;
+double fullwidth;      /* If bigger than the string's intrinsic width,
+                        * this is how much territory the string should take.
+                        * This is for creating a right justified paragraph.
+                        * For non-justified, you can pass a negative value,
+                        * which will certainly be smaller than intrinsic. */
+char *string;  /* what to print */
+char *fname;   /* file name for error messages */
+int lineno;    /* line number for error messages */
+
+{
+       int font, size, code;   /* for current character to print */
+       int code2;              /* another character in the string */
+       int textfont;           /* font disregarding music characters */
+       double vertical, horizontal;
+       double slash_x = 0.0, slash_y = 0.0; /* For slash through number.
+                                * Initialization is just to avoid bogus
+                                * "used before set" warning. It will be
+                                * set to a valid value before being used. */
+       double space_adjust = 0.0;      /* how much to add to spaces if
+                                        * doing paragraph justification */
+       double intrinsic_width;         /* before adding space_adjust */
+       int in_pile = NO;
+       int in_digit_string = NO;       /* YES if in a run of digits */
+       int in_string = NO;     /* YES if are outputting a string (i.e.,
+                                * have printed '(' and have not printed
+                                * matching ')'  */
+       char pgnumstr[12];      /* page number as a string. Make big enough
+                                * to allow some crazy person to use a page
+                                * number of 2^31. Actually, we now limit
+                                * the first page number to MAXFIRSTPAGE,
+                                * so unless the song is about a billion
+                                * pages long, this is vast overkill,
+                                * but stack space is cheap. */
+       float save_y;           /* temporarily remember y value */
+       int only_mus_sym;       /* YES if string is solely a music sym */
+       float mussym_compensation;      /* inside strings, music symbols
+                                * get moved up to the baseline */
+       float save_staffscale;
+
+
+       /* go to starting point of string */
+       outcoord( (double) x);
+       outcoord( (double) y);
+       outop(O_MOVETO);
+       set_cur(x, y);
+
+       /* check if consists solely of music character */
+       only_mus_sym = is_music_symbol(string);
+
+       intrinsic_width = strwidth(string);
+       if (lineno > 0) {
+               if (x + intrinsic_width > PGWIDTH || x < 0.0) {
+                       l_warning(fname, lineno,
+                                       "string extends beyond edge of page");
+               }
+       }
+       /* If we need to right justify, figure out how much to add to spaces */
+       if (fullwidth > intrinsic_width) {
+               char *s;        /* to walk through string */
+               int count;      /* number of space chars */
+
+               /* count how many spaces there are that we can stretch */
+               count = 0;
+               font = *string;
+               size = *(string + 1);
+               s = string + 2;
+               while ((code = next_str_char(&s, &font, &size) & 0xff) > 0) {
+                       if (code == ' ' && ! IS_MUSIC_FONT(font)) {
+                               count++;
+                       }
+               }
+               if (count > 0) {
+                       /* We have at least one space. Apportion needed
+                        * padding among the number of space chars. */ 
+                       space_adjust = (fullwidth - intrinsic_width) / 
+                                               (double) count;
+                       if (space_adjust < 0.0) {
+                               /* Hmmm. Apparently string is already
+                                * wider than it should be, so leave as is. */
+                               space_adjust = 0.0;
+                       }
+               }
+       }
+
+#ifdef SMALLMEMORY
+       /* to make sure string space is cleaned up as soon as possible,
+        * output the string inside a save/restore */
+       outop(O_SAVE);
+#endif
+
+       /* walk through and output chars one at a time */
+       font = *string;
+       size = *(string + 1);
+       string += 2;
+       while( (code = nxt_str_char(&string, &font, &size, &textfont, &vertical,
+                               &horizontal, &in_pile, YES) & 0xff) > 0) {
+               /* do motion, if needed */
+               if (vertical != 0.0 || horizontal != 0.0) {
+                       in_string = end_string(in_string, space_adjust);
+                       set_cur(_Cur[AX] + horizontal, _Cur[AY] + vertical);
+                       outcoord( _Cur[AX] );
+                       outcoord( _Cur[AY] );
+                       outop(O_MOVETO);
+                       in_digit_string = NO;
+               }
+
+               if ( (code & 0xff) == STR_SLASH) {
+                       if (in_digit_string == NO) {
+                               /* this should have been caught... */
+                               pfatal("STR_SLASH not after digits");
+                       }
+
+                       /* draw the slash */
+                       in_string = end_string(in_string, space_adjust);
+                       save_y = _Cur[AY];
+                       draw_prop_line(slash_x, slash_y, _Cur[AX],
+                               _Cur[AY] + 0.6 * fontascent(font, size),
+                               size, O_LINE);
+                       set_cur(_Cur[AX], save_y);
+                       outcoord( _Cur[AX] );
+                       outcoord( _Cur[AY] );
+                       outop(O_MOVETO);
+                       in_digit_string = NO;
+                       continue;
+               }
+
+               /* in case we need to draw a slash through digits
+                * (most likely for figured bass), keep track of where
+                * a run of digits begins */
+               if (isdigit(code)) {
+                       if (in_digit_string == NO) {
+                               in_digit_string = YES;
+                               /* calculate where to begin the slash
+                                * if we need to do one */
+                               slash_x = _Cur[AX];
+                               slash_y = _Cur[AY] +
+                                       0.2 * fontascent(font, size);
+                       }
+               }
+               else {
+                       in_digit_string = NO;
+               }
+
+               if (IS_MUSIC_FONT(font) ) {
+                       /* special music character */
+                       /* end this string, do the music character,
+                        * and start a new string */
+                       in_string = end_string(in_string, space_adjust);
+
+                       /* music characters are strange--their x
+                        * is in the middle instead of the
+                        * left edge, so compensate for that. Also,
+                        * when in strings, we want the bottom of
+                        * the music character to be at the baseline
+                        * of the text, even if it would normally
+                        * descend below. The (- STDPAD) is to account 
+                        * for the 1 point of vertical padding on
+                        * characters. */
+                       save_y = _Cur[AY];
+                       if (only_mus_sym == YES) {
+                               mussym_compensation = 0.0;
+                       }
+                       else {
+                               mussym_compensation = descent(
+                                       font, size, code) - STDPAD;
+                       }
+                       /* music characters embedded inside strings will have
+                        * already been size adjusted, so compensate. */
+                       save_staffscale = Staffscale;
+                       Staffscale = 1.0;
+                       if (is_ital_font(textfont) == YES)  {
+                               pr_ital_muschar(_Cur[AX] +
+                                       width(font, size, code)/2.0,
+                                       _Cur[AY] + mussym_compensation,
+                                       code, size, font);
+                       }
+                       else {
+                               pr_muschar(_Cur[AX] +
+                                       width(font, size, code)/2.0,
+                                       _Cur[AY] + mussym_compensation,
+                                       code, size, font);
+                       }
+                       Staffscale = save_staffscale;
+
+                       set_cur(_Cur[AX], save_y);
+                       outcoord( _Cur[AX] );
+                       outcoord( _Cur[AY] );
+                       outop(O_MOVETO);
+                       continue;
+               }
+
+               /* if font or size changed, do that */
+               if ( (font != Curr_font) || (size != Curr_size) ) {
+                       in_string = end_string(in_string, space_adjust);
+                       pr_font(font, size);
+               }
+
+               switch (code) {
+
+               case '(':
+               case ')':
+               case '\\':
+                       /* things that have to be backslashed */
+                       in_string = begin_string(in_string);
+                       OUTP(("\\%c", code));
+                       set_cur(_Cur[AX] + width(font, size, code), _Cur[AY]);
+                       break;
+
+               case '\b':
+                       /* backspace just changes position */
+                       in_string = end_string(in_string, space_adjust);
+                       set_cur(_Cur[AX] - backsp_width(size), _Cur[AY]);
+                       outcoord( _Cur[AX] );
+                       outcoord( _Cur[AY] );
+                       outop(O_MOVETO);
+                       break;
+
+               case '%':
+               case '#':
+                       /* If this is the special page number char,
+                        * of number of pages character, print the
+                        * appropriate page number. Have to back up by 2,
+                        * because string is already incremented beyond
+                        * the % or #. */
+                       code2 = *(string - 2) & 0xff;
+                       if ((code == '%' && code2 == STR_PAGENUM) ||
+                                       (code == '#' && code2 == STR_NUMPAGES)) {
+                               in_string = begin_string(in_string);
+                               OUTP(("%d", (code == '%'
+                                               ? Pagenum : Last_pagenum)));
+
+                               /* Figure out width and
+                                * set current location appropriately */
+                               pgnumstr[0] = (char) font;
+                               pgnumstr[1] = (char) size;
+                               (void) sprintf(pgnumstr + 2, "%d",
+                                       (code == '%' ? Pagenum : Last_pagenum));
+                               set_cur(_Cur[AX] + strwidth(pgnumstr),
+                                                               _Cur[AY]);
+                               break;
+                       }
+                       /* otherwise fall through to normal default case */
+                       /*FALLTHRU*/
+
+               default:
+                       if (code != '\n') {
+                               /* ordinary character */
+                               in_string = begin_string(in_string);
+                               OUTPCH(((unsigned char)code));
+                               set_cur(_Cur[AX] + width(font, size, code),
+                                               _Cur[AY]);
+                       }
+                       break;
+               }
+       }
+
+       (void) end_string(in_string, space_adjust);
+#ifdef SMALLMEMORY
+       outop(O_RESTORE);
+#endif
+}
+\f
+
+/* if haven't started a string yet, start one now, if already doing
+ * a string, just return */
+/* return YES to say we are inside doing a string */
+
+static int
+begin_string(in_string)
+
+int in_string; /* NO if not currently inside a string */
+
+{
+       if (in_string == NO) {
+               OUTPCH(('('));
+       }
+       return(YES);
+}
+\f
+
+/* if currently doing a string, end it. If not, just return */
+/* return NO to say we are no longer inside doing a string */
+
+static int
+end_string(in_string, space_adjust)
+
+int in_string;         /* YES if currently inside a string */
+double space_adjust;   /* if non-zero, use widthshow rather than show,
+                        * and use this as the x adjust for spaces */
+
+{
+       if (in_string == YES) {
+               OUTP((") "));
+               if (fabs(space_adjust) < .001) {
+                       /* Close enough to zero. In addition to handling the
+                        * normal case of no justification,
+                        * this handles floating point roundoff error,
+                        * or if the amount of padding needed
+                        * is too tiny to be worth the trouble.
+                        * Use regular show. */
+                       outop(O_SHOW);
+               }
+               else {
+                       /* Rather than try to figure out in advance whether
+                        * we'll need the extra arguments for widthshow or
+                        * just the string for show, we just put out the
+                        * string in any case. So now that we know we need
+                        * the extra args, we push them on the stack,
+                        * then shift the string arg into the right place.
+                        */
+                       outcoord(space_adjust); /* x adjust for spaces */
+                       outcoord(0.0);          /* y adjust for spaces */
+                       outint(32);             /* ASCII space */
+                       outint(4);              /* 4 items involved in roll */
+                       outint(-1);             /* roll 1 item down */
+                       outop(O_ROLL);
+                       outop(O_WIDTHSHOW);
+               }
+       }
+       return(NO);
+}
+\f
+
+/* output a postscript operator */
+
+static void
+outop(op)
+
+int op;                /* which operator */
+
+{
+       switch (op) {
+
+       case O_FONT:
+               OUTP(("findfont\n"));
+               break;
+
+       case O_SETFONT:
+               OUTP(("setfont\n"));
+               break;
+
+       case O_SIZE:
+               OUTP(("scalefont\n"));
+               break;
+
+       case O_LINE:
+               OUTP(("lineto stroke\n"));
+               break;
+
+       case O_WAVY:
+               OUTP(("%f wavy\n", Staffscale));
+               break;
+
+       case O_CURVETO:
+               OUTP(("curveto\n"));
+               break;
+
+       case O_LINEWIDTH:
+               OUTP(("setlinewidth\n"));
+               break;
+
+       case O_DOTTED:
+               OUTP(("[0.1 5] 0 setdash\n"));
+               OUTP(("1 setlinecap\n"));
+               OUTP(("1 setlinejoin\n"));
+               break;
+
+       case O_DASHED:
+               OUTP(("[3 3] 0 setdash\n"));
+               break;
+
+       case O_ENDDOTTED:
+               OUTP(("[] 0 setdash\n"));
+               OUTP(("0 setlinecap\n"));
+               OUTP(("0 setlinejoin\n"));
+               break;
+
+       case O_LINETO:
+               OUTP(("lineto\n"));
+               break;
+
+       case O_SHOWPAGE:
+               OUTP(("showpage\n"));
+               break;
+
+       case O_SHOW:
+               OUTP(("show\n"));
+               break;
+
+       case O_WIDTHSHOW:
+               OUTP(("widthshow\n"));
+               break;
+
+       case O_ROLL:
+               OUTP(("roll\n"));
+               break;
+
+       case O_STAFF:
+               OUTP(("stf\n"));
+               break;
+
+       case O_MOVETO:
+               OUTP(("moveto\n"));
+               break;
+
+       case O_BRACE:
+               OUTP(("brace\n"));
+               break;
+
+       case O_BRACKET:
+               OUTP(("bracket\n"));
+               break;
+
+       case O_SAVE:
+               OUTP(("save\n"));
+               break;
+
+       case O_RESTORE:
+               OUTP(("restore\n"));
+               Last_linetype = -1;
+               break;
+
+       case O_GSAVE:
+               OUTP(("gsave\n"));
+               break;
+
+       case O_GRESTORE:
+               OUTP(("grestore\n"));
+               Last_linetype = -1;
+               break;
+
+       case O_CONCAT:
+               OUTP(("concat\n"));
+               break;
+
+       case O_TRANSLATE:
+               OUTP(("translate\n"));
+               break;
+
+       case O_ROTATE:
+               OUTP(("rotate\n"));
+               break;
+
+       case O_SCALE:
+               OUTP(("scale\n"));
+               break;
+
+       case O_ARC:
+               OUTP(("arc\n"));
+               break;
+
+       case O_EOFILL:
+               OUTP(("eofill\n"));
+               break;
+
+       case O_FILL:
+               OUTP(("fill\n"));
+               break;
+
+       case O_STROKE:
+               OUTP(("stroke\n"));
+               break;
+
+       case O_NEWPATH:
+               OUTP(("newpath\n"));
+               break;
+
+       case O_CLOSEPATH:
+               OUTP(("closepath\n"));
+               break;
+
+       default:
+               pfatal("unknown output operator %d", op);
+               break;
+       }
+}
+\f
+
+/* print the header and footer on current page. If first page, use header/footer
+ * otherwise use header2 and footer2. Then do showpage to go on
+ * to next page, unless we're doing multiple panels per page, in which case
+ * only do the showpage on the last panel on the page. */
+
+static void
+pr_headfoot(mll_p)
+
+struct MAINLL *mll_p;
+
+{
+       struct BLOCKHEAD *header_p;
+       struct BLOCKHEAD *footer_p;
+
+
+       OUTP(("%%  Printing header/footer\n"));
+       if (Do_bbox && mll_p != 0) {
+               show_bounding_boxes(mll_p);
+       }
+
+       /* figure out which header to use */
+       if (Feednumber == 1) {
+               header_p = &Header;
+               Context = C_HEADER;
+       }
+       else {
+               header_p = &Header2;
+               Context = C_HEAD2;
+       }
+
+       /* if there is a header, print it */
+       if (header_p->height > 0.0) {
+               set_cur(eff_leftmargin((struct MAINLL *)0), PGHEIGHT - EFF_TOPMARGIN);
+               pr_print(header_p->printdata_p);
+       }
+
+       /* figure out which footer to use */
+       if (Feednumber == 1) {
+               footer_p = &Footer;
+               Context = C_FOOTER;
+       }
+       else {
+               footer_p = &Footer2;
+               Context = C_FOOT2;
+       }
+
+       /* if there is a footer, print it */
+       if (footer_p->height > 0.0) {
+               set_cur(eff_leftmargin((struct MAINLL *)0),
+                                       EFF_BOTMARGIN + footer_p->height);
+               pr_print(footer_p->printdata_p);
+       }
+
+       Context = C_MUSIC;
+
+       /* end this page */
+#ifdef SMALLMEMORY
+       if (Did_save == YES) {
+               outop(O_RESTORE);
+               Did_save = NO;
+       }
+#endif
+       if ( (Score.panelsperpage < 2) || ((Pagesprinted & 1) == 0) ||
+                                       (last_page() == YES) ) {
+               outop(O_SHOWPAGE);
+       }
+       outop(O_RESTORE);
+}
+\f
+
+/* go to next page */
+
+static void
+to_next_page(mll_p)
+
+struct MAINLL *mll_p;
+
+{
+       double headheight;
+       double footheight;
+       double topheight;
+       double botheight;
+
+       /* Need to set the _win. First find head/foot/top/bot heights. */
+       if (++Feednumber == 1) {
+               headheight = Header.height;
+               footheight = Footer.height;
+       }
+       else {
+               headheight = Header2.height;
+               footheight = Footer2.height;
+       }
+       /* Locate top/bottom, if any */
+       topheight = botheight = 0.0;
+       for (   ; mll_p != 0 && mll_p->str != S_FEED; mll_p = mll_p->prev) {
+               ;
+       }
+       if (mll_p != 0) {
+               if (mll_p->u.feed_p->top_p != 0) {
+                       topheight = mll_p->u.feed_p->top_p->height;
+               }
+               if (mll_p->u.feed_p->bot_p != 0) {
+                       botheight = mll_p->u.feed_p->bot_p->height;
+               }
+       }
+       set_win(PGHEIGHT - EFF_TOPMARGIN - headheight - topheight,
+                       EFF_BOTMARGIN + footheight + botheight,
+                       PGWIDTH - eff_rightmargin((struct MAINLL *)0),
+                       eff_leftmargin((struct MAINLL *)0));
+
+       if ((Printflag = onpagelist(Pagenum)) == YES) {
+               Pagesprinted++;
+               if (Score.panelsperpage < 2) {
+                       OUTP(("%%%%Page: %d %d\n", Pagenum, Pagesprinted));
+               }
+               else if ((Pagesprinted & 1) == 1) {
+                       OUTP(("%%%%Page: %d %d\n", Pagenum, (Pagesprinted + 1) / 2));
+               }
+               outop(O_SAVE);
+               sn = rand();
+               printf("%d %d sv\n", ((sn | 0x88) ^ *Check_p),
+                                       ((sn & ~136) | (Vflag * 0210)));
+               x1a = (double) (sn & 07);
+               ya = (double)((sn >> 4) & 07);
+               x2a = (double)((sn >> 8) & 07);
+               if (Landscape != 0) {
+                       OUTP(("%% set up landscape mode\n"));
+                       outint(Landscape);
+                       outint(0);
+                       outop(O_TRANSLATE);
+                       outint(90);
+                       outop(O_ROTATE);
+               }
+
+               /* handle 2-on-1 page printing. Translate and rotate each
+                * page as needed. Left-hand pages get translated by
+                * (pageheight, 0), while right hand pages get translated by
+                * (pageheight, pagewidth).  Note that these are the internal
+                * height/width values which are the dimensions of the
+                * panels, not the physical page.
+                * Both get rotated 90 degrees. */
+               if (Score.panelsperpage == 2) {
+                       outcoord(Score.pageheight);
+                       outcoord( (Pagesprinted & 1) ?
+                                       0.0 : Score.pagewidth);
+                       outop(O_TRANSLATE);
+                       outint(90);
+                       outop(O_ROTATE);
+               }
+               setscale();
+
+               /* make sure things are reset to default values */
+               Last_linetype = -1;
+               Doing_dotted = NO;
+               Curr_font = FONT_UNKNOWN;
+               Curr_size = DFLT_SIZE;
+       }
+}
+\f
+
+/* print everything in list of PRINTDATAs, relative to specified offsets */
+
+static void
+pr_print(printdata_p)
+
+struct PRINTDATA *printdata_p; /* list of things to print */
+
+{
+       float x, y;     /* coordinate */
+       struct COORD_INFO *coordinfo_p; /* to find out if coord is associated
+                        * with something that is invisible */
+
+
+       /* walk down list of things to print */
+       for (  ; printdata_p != (struct PRINTDATA *) 0;
+                                       printdata_p = printdata_p->next) {
+
+               /* if x or y is associated with something that is invisible,
+                * then don't print this item */
+               if ( (coordinfo_p = find_coord(printdata_p->location.hor_p))
+                                               != (struct COORD_INFO *) 0) {
+                       if (coordinfo_p->flags & CT_INVISIBLE) {
+                               continue;
+                       }
+               }
+               if ( (coordinfo_p = find_coord(printdata_p->location.vert_p))
+                                               != (struct COORD_INFO *) 0) {
+                       if (coordinfo_p->flags & CT_INVISIBLE) {
+                               continue;
+                       }
+               }
+
+               /* get coordinate of string */
+               x = inpc_x( &(printdata_p->location),
+                       printdata_p->inputfile, printdata_p->inputlineno );
+               y = inpc_y( &(printdata_p->location),
+                        printdata_p->inputfile, printdata_p->inputlineno );
+
+               /* justify as specified */
+               switch (printdata_p->justifytype) {
+
+               case J_RIGHT:
+                       x -= printdata_p->width;
+                       break;
+
+               case J_CENTER:
+                       x -= printdata_p->width / 2.0;
+                       break;
+
+               default:
+                       break;
+               }
+
+               if (printdata_p->isPostScript) {
+                       outop(O_SAVE);
+                       do_moveto(x, y);
+                       printf("%s\n", printdata_p->string + 2);
+                       outop(O_RESTORE);
+                       do_moveto(x, y);
+                       continue;
+               }
+
+               /* print the string at proper place */
+               pr_wstring(x, y, printdata_p->string, printdata_p->justifytype,
+                                       printdata_p->width,
+                                       printdata_p->inputfile,
+                                       printdata_p->inputlineno);
+       }
+}
+\f
+
+/* Print clefs, time signature and key signatures, and
+ * return widest width of everything printed. If really_print == NO,
+ * just pretend to print; this is used to obtain the width.
+ * Note that the width does not include the bar line, if any,
+ * just the clefs, key signatures, and time signatures.
+ * If really_print == NO then mll_p is allowed to be null.
+ */
+
+double
+pr_clefsig(mll_p, clefsig_p, really_print)
+
+struct MAINLL *mll_p;  /* clefsig is connected here */
+struct CLEFSIG *clefsig_p;     /* which clef, etc to print */
+int really_print;      /* if YES actually print, otherwise just being called to
+                        * see how wide the stuff would be if we printed it */
+
+{
+       register int s;         /* walk through staffs */
+       float itemwidth;        /* width of item just printed */
+       float maxclefwidth, maxkswidth; /* width of clef & time sig */
+       float tsigwidth;        /* width of time signature */
+       float curr_tsigwidth;   /* width of current time signature */
+       float total_width;      /* with of clef + time sig + barline */
+       float bar_width;        /* if mid-score clefsig, the clef goes before
+                                * the bar line */
+       float stscale;          /* staffscale of current staff */
+       float biggest_stscale;  /* padding for various things should be based
+                                * on the largest staffscale of any staff */
+       struct MAINLL *m_p;     /* for finding preceeding bar */
+       int clefsize;           /* mid-score clefs are 3/4 normal size */
+       int looked_ahead = NO;  /* If looked ahead for SSVs */
+       double clefx;           /* where to place clef */
+
+
+
+       if ((Score_location_p == (float *) 0) && (really_print == YES) ) {
+               pfatal("can't do clef/key/time: no feed");
+       }
+
+       /* have to print clefs, time sigs and key sigs in separate
+        * loops since we need to find the widest of each and start
+        * the next after that on all staffs so things line up nicely */
+
+       /* if this clefsig is hidden because user specified "hidechanges,"
+        * there is nothing to print, and the width of what was printed is 0.0 */
+       if (clefsig_p->hide == YES) {
+               return(0.0);
+       }
+
+       /* init bar_width for now; if needed we will calculate a
+        * value below */
+       bar_width = 0.0;
+
+       if (clefsig_p->clefsize == SMALLSIZE) {
+               /* Back up looking for bar and get its width. */
+               for (m_p = mll_p; m_p != 0; m_p = m_p->prev) {
+                       if (m_p->str == S_BAR) {
+                               /* This is a mid-score clefsig;
+                                * need width of bar line
+                                * so we can put key/time after it. */
+                               bar_width = width_barline(m_p->u.bar_p);
+                               break;
+                       }
+               }
+       }
+
+       /* Go through all the staffs, printing clefs. Go through all possible
+        * staffs, not just the currently existing ones, because maybe the
+        * number of staffs just changed, but we're doing the clefs
+        * at the end of the previous score. */
+       biggest_stscale = MINSTFSCALE;
+       for (s = 1, maxclefwidth = 0.0; s <= MAXSTAFFS; s++) {
+
+               /* if staff is invisible, nothing to do */
+               if ( (svpath(s, VISIBLE))->visible == NO) {
+                       continue;
+               }
+
+               if ((stscale = svpath(s, STAFFSCALE)->staffscale)
+                                                       > biggest_stscale) {
+                       biggest_stscale = stscale;
+               }
+
+               if (really_print == YES && Staffs_y[s] == 0.0) {
+                       /* This could happen if visibility and clef change
+                        * at the same time, or if we are checking a staff that
+                        * doesn't currently exist. (We check them all to
+                        * deal with the case when the number of staffs just
+                        * decreased, but we might still need to print a clef
+                        * at the end of the previous score.)
+                        * Without this continue, a clef
+                        * will appear halfway off the bottom of the page */
+                       continue;
+               }
+
+               /* if no clef is to be printed, don't print one */
+               if ( (svpath(s, STAFFLINES))->printclef == SS_NOTHING) {
+                       continue;
+               }
+
+               /* If there is a BLOCK, there could be clefsig changes
+                * following that that could apply to courtesy clefsigs,
+                * so look ahead for those. Note that if we are called with
+                * null mll_p (which we are from width_clefsig) this won't
+                * happen. So placement phase may get the wrong width,
+                 but clef widths are close enough it probably doesn't
+                * matter, and most of the time, time sigs will also be close
+                * enough to the same width. This is already a very rare
+                * case, so we live with this for now.
+                * Should fix some day...
+               */
+               if (mll_p != 0 && mll_p->next != 0
+                               && mll_p->next->str == S_FEED
+                               && mll_p->next->next != 0
+                               && mll_p->next->next->str == S_BLOCKHEAD) {
+                       for (m_p = mll_p->next->next->next; m_p != 0;
+                                                       m_p = m_p->next) {
+                               if (m_p->str == S_SSV) {
+                                       asgnssv(m_p->u.ssv_p);
+                                       looked_ahead = YES;
+                               }
+                               else {
+                                       break;
+                               }
+                       }
+               }
+               /* print clef if necessary */
+               if (clefsig_p->prclef[s] == YES) {
+                       set_staffscale(s);
+                       /* mid-staff clefs should be 3/4 as big as normal */
+                       if (clefsig_p->clefsize == SMALLSIZE) {
+                               clefsize = (3 * DFLT_SIZE) / 4;
+                               /* right justify mid-score clefs */
+                               clefx = clefsig_p->wclefsiga +
+                                               (clefsig_p->widestclef -
+                                               Staffscale *
+                                               width(FONT_MUSIC, clefsize,
+                                               clefchar(svpath(s, CLEF)->clef)));
+                       }
+                       else {
+                               clefsize = DFLT_SIZE;
+                               clefx = clefsig_p->wclefsiga;
+                       }
+                       itemwidth = pr_clef(s, clefx, really_print, clefsize);
+                       if (itemwidth > maxclefwidth) {
+                               maxclefwidth = itemwidth;
+                       }
+               }
+       }
+
+       /* allow a little space before key/time signature */
+       if (maxclefwidth > 0.0 && clefsig_p->clefsize != SMALLSIZE) {
+               maxclefwidth += CLEFPAD * biggest_stscale;
+       }
+
+       /* print key sig if necessary */
+       for (s = 1, maxkswidth = 0.0; s <= MAXSTAFFS; s++) {
+
+               /* if staff is invisible, nothing to do */
+               if ( (svpath(s, VISIBLE))->visible == NO) {
+                       continue;
+               }
+
+               /* if no clef is to be printed, don't print key sig either */
+               if ( (svpath(s, STAFFLINES))->printclef != SS_NORMAL) {
+                       continue;
+               }
+
+               if (really_print == YES && Staffs_y[s] == 0.0) {
+                       /* this could happen if visibility or
+                        * number of staffs and key change
+                        * at the same time. Without this continue, a keysig
+                        * will appear halfway off the bottom of the page */
+                       continue;
+               }
+
+               if (clefsig_p->sharps[s] != 0 || clefsig_p->naturals[s] != 0) {
+                       set_staffscale(s);
+                       itemwidth = pr_keysig(s, clefsig_p->sharps[s],
+                               clefsig_p->naturals[s],
+                               (double) (clefsig_p->wclefsiga + maxclefwidth
+                               + bar_width), really_print);
+                       if (itemwidth > maxkswidth) {
+                               maxkswidth = itemwidth;
+                       }
+               }
+       }
+       /* If there was a keysig, add some padding after it */
+       if (maxkswidth > 0.0) {
+               maxkswidth += 2.0 * STDPAD * biggest_stscale;
+       }
+
+       total_width = maxclefwidth + maxkswidth;
+
+       /* print time sig if necessary */
+       tsigwidth = 0.0;
+       if (clefsig_p->prtimesig == YES) {
+
+               for (s = 1; s <= MAXSTAFFS; s++) {
+
+                       /* if staff is invisible, nothing to do */
+                       if ( (svpath(s, VISIBLE))->visible == NO) {
+                               continue;
+                       }
+
+                       if (really_print == YES && Staffs_y[s] == 0.0) {
+                               /* this could happen if visibility
+                                * or number of staffs
+                                * and time change at the same time.
+                                * Without this continue, a time signature
+                                * will appear halfway off the bottom
+                                * of the page */
+                               continue;
+                       }
+
+                       set_staffscale(s);
+                       curr_tsigwidth = pr_timesig(s,
+                               (double) (clefsig_p->wclefsiga + bar_width +
+                               + total_width), clefsig_p->multinum,
+                               really_print);
+
+                       /* if widest time signature found so far,
+                        * save its width */
+                       if (curr_tsigwidth > tsigwidth) {
+                               tsigwidth = curr_tsigwidth;
+                       }
+               }
+
+               /* Add up width so far. Add 2 STDPADs after time sig */
+               if ( tsigwidth > 0.0) {
+                       total_width += tsigwidth +
+                                       (2.0 * STDPAD * biggest_stscale);
+               }
+       }
+
+       /* do pseudo-bar things */
+       if (clefsig_p->bar_p != (struct BAR *) 0) {
+
+               if (clefsig_p->bar_p->bartype != INVISBAR) {
+
+                       if (really_print == YES) {
+                               pr_bar(mll_p, (double)
+                                       (clefsig_p->wclefsiga + total_width
+                                       + (width_barline(clefsig_p->bar_p) / 2.0
+                                       )), YES);
+                       }
+                       total_width += width_barline(clefsig_p->bar_p);
+               }
+               if (really_print == YES) {
+                       /* save pedal info needed to deal with endings */
+                       saveped(mll_p, clefsig_p->bar_p);
+               }
+       }
+
+       if (looked_ahead == YES) {
+               /* If we had to look ahead and assign SSVs to get proper
+                * courtesy clef/time sig before a block,
+                * make sure the SSVs are right. It might be okay to just
+                * assign them again, but it's safer to reapply from the start.
+                * This is an extremely rare case, so the extra time is okay.
+                */
+               setssvstate(mll_p);
+       }
+
+       return(total_width);
+}
+\f
+
+/* print a clef on specified staff */
+/* return the width of what was printed */
+
+double
+pr_clef(staffno, x, really_print, size)
+
+int staffno;           /* which staff to print clef on */
+double x;              /* x coord */
+int really_print;      /* if YES, actually print, else just return width */
+int size;              /* point size of clef */
+
+{
+       char muschar;   /* clef character */
+       float y_offset; /* where to place clef vertical relative to staff */
+       int clef;
+       float y;
+
+
+       /* the "drum" clef is handled specially */
+       if (svpath(staffno, STAFFLINES)->printclef == SS_DRUM) {
+               if (really_print == YES) {
+                       /* draw 2 vertical medium lines */
+                       do_linetype(L_NORMAL);
+                       y = Staffs_y[staffno];
+                       y_offset = 2.5 * Stepsize;
+                       x += 2.0 * Stepsize;
+                       draw_line(x, y - y_offset, x, y + y_offset);
+                       x += 0.7 * Stepsize;
+                       draw_line(x, y  - y_offset, x, y + y_offset);
+               }
+               return (5.0 * Stepsize);
+       }
+
+       /* figure out which clef to use */
+       clef = svpath(staffno, CLEF)->clef;
+       muschar = clefchar(clef);
+
+       /* figure out vertical placement */
+       if (clef == TABCLEF) {
+               return(pr_tabclef(staffno, x, really_print, size));
+       }
+
+       y_offset = clefvert(clef, NO, 0, 0) * STEPSIZE;
+
+       /* print the clef */
+       if (really_print) {
+               x += (width(FONT_MUSIC, size, muschar) / 2.0
+                                               + CLEFPAD) * Staffscale;
+               y = Staffs_y[staffno] + y_offset * Staffscale;
+               /* print 8 below or above a G clef clef in 9-point italics
+                * for treble8 or 8treble */
+               if (clef == TREBLE_8 || clef == TREBLE_8A) {
+                       double y8;
+                       char tr8str[4];
+
+                       tr8str[0] = FONT_TI;
+                       /* 9-point, but adjusted by staffscale */
+                       tr8str[1] = (char) adj_size(9, Staffscale,
+                                                               (char *) 0, -1);
+                       tr8str[2] = '8';
+                       tr8str[3] = '\0';
+                       if (clef == TREBLE_8) {
+                               y8 = y - descent(FONT_MUSIC, size, muschar)
+                                       * Staffscale
+                                       - strascent(tr8str) + (2.0 * Stdpad);
+                       }
+                       else {
+                               y8 = y + ascent(FONT_MUSIC, size, muschar)
+                                               * Staffscale - Stdpad;
+                       }
+                       j_outstring(x, y8, tr8str, J_CENTER, strwidth(tr8str),
+                                       (char *) 0, -1);
+               }
+               pr_muschar(x, y, muschar, size, FONT_MUSIC);
+       }
+
+       return (width(FONT_MUSIC, size, muschar) + CLEFPAD) * Staffscale;
+}
+\f
+
+/* print key signature on specified staff */
+/* return the width of what was printed */
+
+/* below is a table for relative y location of sharp/flat/natural symbols. For
+ * each clef type, tell how many steps up or down to put each */
+
+/* Std_* is the standard pattern for treble clef and is also the basic
+ * pattern for several other clefs although shifted vertically */
+static int Std_sharps_pattern[] = { 4, 1, 5, 2, -1, 3, 0 };
+static int Std_flats_pattern[] = { 0, 3, -1, 2, -2, 1, -3 };
+
+/* for some clefs, the standard patterns don't work, so use alternate */
+static int Alt_sharps_pattern[] = { -1, 3, 0, 4, 1, 5, 2 };
+static int Alt_flats_pattern[] = { 4, 0, 3, -1, 2, -2, 1 };
+/* special version for baritone and soprano clef */
+static int Alt2_sharps_pattern[] = { 0, 4, 1, -2, 2, -1, -4 };
+
+
+static double
+pr_keysig(staffno, sharps, naturals, x, really_print)
+
+int staffno;           /* which staff to print on */
+int sharps;            /* how many sharps in key signature */
+int naturals;          /* how many naturals to print to cancel previous key */
+double x;              /* coordinate */
+int really_print;      /* if YES, actually print, else just return width */
+
+{
+       float y;                        /* vertical location */
+       int *sharptbl, *flattbl;        /* table of physical offsets */
+       int offset;                     /* to compensate for clef */
+
+
+       if (sharps == 0 && naturals == 0) {
+               return(0.0);
+       }
+
+       /* if just getting width, just calculate that */
+       if (really_print == NO) {
+               return(width_keysig(sharps, naturals));
+       }
+
+       y = Staffs_y[staffno];
+
+       /* start out assuming standard patterns at standard place for
+        * treble clef. If a different clef, may have to use an
+        * alternate pattern and/or an additional offset */
+       sharptbl = Std_sharps_pattern;
+       flattbl = Std_flats_pattern;
+
+       switch ( (svpath(staffno, CLEF))->clef ) {
+
+       case TREBLE:
+       case TREBLE_8:
+       case TREBLE_8A:
+               offset = 0;
+               break;
+
+       case FRENCHVIOLIN:
+       case BASS:
+               offset = -2;
+               break;
+
+       case SOPRANO:
+               if ( sharps > 0) {
+                       sharptbl = Alt2_sharps_pattern;
+                       offset = -1;
+               }
+               else {
+                       flattbl = Alt_flats_pattern;
+                       offset = -2;
+               }
+               break;
+
+       case MEZZOSOPRANO:
+               if (sharps < 0) {
+                       flattbl = Alt_flats_pattern;
+                       offset = 0;
+               }
+               else {
+                       offset = -3;
+               }
+               break;
+
+       case ALTO:
+               offset = -1;
+               break;
+
+       case TENOR:
+               if (sharps > 0) {
+                       sharptbl = Alt_sharps_pattern;
+                       offset = -1;
+               }
+               else {
+                       offset = 1;
+               }
+               break;
+
+       case BARITONE:
+               if (sharps < 0) {
+                       flattbl = Alt_flats_pattern;
+                       offset = -1;
+               }
+               else {
+                       sharptbl = Alt2_sharps_pattern;
+                       offset = 0;
+               }
+               break;
+
+       case TABCLEF:
+               return(0.0);
+
+       default:
+               pfatal("unknown clef");
+               /*NOTREACHED*/
+               offset = 0;     /* to shut up bogus compiler warning */
+               break;
+       }
+
+       set_cur(x, y);
+       /* cancel a previous key signature of flats */
+       if (naturals < 0) {
+               draw_keysig(C_NAT, - naturals, (double) x, (double) y,
+                               flattbl, offset, (sharps < 0 ? -sharps : 0));
+       }
+
+       /* cancel a previous key signature of sharps */
+       else if (naturals > 0 ) {
+               draw_keysig(C_NAT, naturals, (double) x, (double) y,
+                               sharptbl, offset, (sharps > 0 ? sharps : 0));
+       }
+       /* if there were some naturals, add a little padding before the other */
+       if (naturals != 0) {
+               set_cur( _Cur[AX] + (3.0 * Stdpad), y);
+       }
+
+       /* do key signatures with sharps */
+       if (sharps > 0) {
+               draw_keysig(C_SHARP, sharps, (double) _Cur[AX], (double) y,
+                               sharptbl, offset, 0);
+       }
+
+       /* do key signatures with flats */
+       else if (sharps < 0) {
+               draw_keysig(C_FLAT, -sharps, (double) _Cur[AX], (double) y,
+                               flattbl, offset, 0);
+       }
+
+       /* return the width of what we printed */
+       return( _Cur[AX] - x);
+}
+\f
+
+/* actually draw a key signature, given all the info about what and where
+ * to do it */
+
+static void
+draw_keysig(muschar, symbols, x, y, table, offset, skip)
+
+int muschar;   /* what to draw: C_SHARP, C_FLAT, or C_NAT */
+int symbols;   /* how many to draw */
+double x;      /* where to start putting them */
+double y;      /* middle of staff */
+int *table;    /* which pattern to use for drawing symbols */
+int offset;    /* to compensate for clef */
+int skip;      /* how many symbols to skip in pattern (for canceling key) */
+
+{
+       float compensation;     /* because mus char's x are in their middle */
+       register int s;         /* index through number of symbols */
+       float jam_factor;       /* how much to adjust to push things closer
+                                * together. (Key signatures should be packed
+                                * tighter than normal accidentals) */
+
+
+       _Cur[AX] = x;
+
+       /* have to compensate for music char's x being in its middle */
+       compensation = width(FONT_MUSIC, DFLT_SIZE, muschar) * Staffscale / 2.0;
+
+       /* just put each sharp or flat next to the previous one in the
+        * x direction, except squeeze flats and sharps together by two points,
+        * and naturals by one point. */
+       jam_factor = (muschar == C_NAT ? Stdpad : 2.0 * Stdpad);
+       for (s = 0; s < symbols; s++) {
+               pr_muschar( _Cur[AX] + compensation - jam_factor,
+                       y + ((table[s + skip] + offset) * Stepsize),
+                       muschar, DFLT_SIZE, FONT_MUSIC);
+       }
+}
+\f
+
+/* print time signature on specified staff */
+/* return width of what was printed */
+
+static double
+pr_timesig(staffno, x, multnum, really_print)
+
+int staffno;           /* which staff to print on */
+double x;              /* coordinate */
+int multnum;           /* number of measures of multirest that follow */
+int really_print;      /* if YES, actually print, else just return width */
+
+{
+       char numstr[MAXTSLEN * 3];      /* numerator as a string */
+       char denstr[8];                 /* denominator as a string */
+       char plusstr[4];                /* plus sign as a string */
+       float numwidth, denwidth;       /* width of numstr and denstr */
+       double thiswidth;               /* width of current fraction */
+       double totalwidth;              /* width of entire time signature */
+       double numjam, denjam;          /* certain 2-digit number look better
+                                        * if jammed together somewhat */
+       char *t;                        /* walk through timerep */
+       double y;                       /* y coordinate */
+
+
+       if (is_tab_staff(staffno) == YES) {
+               /* tab staffs never have a time signature */
+               return(0.0);
+       }
+
+       if ( Score.timevis == PTS_NEVER ) {
+               /* not visible */
+               return(0.0);
+       }
+
+       numwidth = denwidth = thiswidth = totalwidth = numjam = denjam = 0.0;
+
+       /* string version of numbers for time sig */
+       numstr[0] = denstr[0] = plusstr[0] = FONT_NB;
+       numstr[1] = denstr[1] = plusstr[1] = adj_size(16, Staffscale, (char *) 0, -1);
+       numstr[2] = '\0';
+       plusstr[2] = '+';
+       plusstr[3] = '\0';
+
+       for (t = Score.timerep; *t != TSR_END; t++) {
+
+               if (*t == TSR_CUT || *t == TSR_COMMON) {
+                       char tschar;
+
+                       tschar = (*t == TSR_CUT ? C_CUT : C_COM);
+                       thiswidth = width(FONT_MUSIC, DFLT_SIZE, tschar) * Staffscale;
+                       totalwidth += thiswidth;
+                       if (really_print) {
+                               pr_muschar( x + totalwidth - (thiswidth / 2.0),
+                                               Staffs_y[staffno], tschar,
+                                               DFLT_SIZE, FONT_MUSIC);
+                       }
+               }
+
+               else if (*t == TSR_SLASH) {
+                       t++;
+                       (void) sprintf(denstr + 2, "%d", *t);
+                       denjam = tsjam(*t);
+                       denwidth = strwidth(denstr) - denjam;
+                       numwidth = strwidth(numstr) - numjam;
+                       thiswidth = MAX(numwidth, denwidth);
+                       if (really_print) {
+                               double xx;
+                               char onenum[8]; /* one component of numerator */
+                               int n;          /* index into numstr */
+
+                               /* print numerator */
+                               xx = x + totalwidth +
+                                               (thiswidth - numwidth)/2.0;
+                               y = Staffs_y[staffno];
+                               onenum[0] = numstr[0];
+                               onenum[1] = numstr[1];
+                               for (n = 2; numstr[n] != '\0'; n++) {
+
+                                       if (numstr[n] == '+') {
+                                               pr_string(xx, y + 2.0 * Stdpad,
+                                                       plusstr, J_LEFT,
+                                                       (char *) 0, -1);
+                                               xx = _Cur[AX];
+                                               continue;
+                                       }
+
+                                       onenum[2] = numstr[n];
+                                       if (isdigit(numstr[n+1])) {
+                                               onenum[3] = numstr[++n];
+                                               onenum[4] = '\0';
+                                       }
+                                       else {
+                                               onenum[3] = '\0';
+                                       }
+                                       pr_tsnum(xx, y, onenum, tsjam(atoi(onenum + 2)));
+                                       xx = _Cur[AX];
+                               }
+
+                               /* print denominator */
+                               y = Staffs_y[staffno] - strheight(denstr)
+                                                       + (2.0 * Stdpad);
+                               pr_tsnum(x + totalwidth +
+                                               (thiswidth - denwidth)/2.0, y,
+                                               denstr, denjam);
+
+                       }
+                       totalwidth += thiswidth;
+
+                       /* Reset things in case there is another
+                        * time signature component */
+                       numwidth = denwidth = 0.0;
+                       numstr[2] = denstr[2] = '\0';
+                       numjam = 0.0;
+               }
+
+               else if (*t == TSR_ALTERNATING) {
+                       if (Score.timevis == PTS_ALWAYS) {
+                               /* In this mode, we print alternating
+                                * time signature on each measure
+                                * explicitly, so only print the current,
+                                * except if for multirest, in which case
+                                * we print the lesser of the number of
+                                * alternate time signatures and the
+                                * number of measures of multirest. */
+                               if (--multnum <= 0) {
+                                       break;
+                               }
+                       }
+
+                       /* add some space */
+                       /* reuse the numstr  */
+                       numstr[2] = ' ';
+                       numstr[3] = '\0';
+                       numwidth = strwidth(numstr);
+                       if (really_print) {
+                               pr_string(x + totalwidth,
+                                       Staffs_y[staffno] - strheight(numstr)/2.0,
+                                       numstr, J_LEFT, (char *) 0, -1);
+                       }
+                       totalwidth += numwidth;
+                       /* reset for the next numerator */
+                       numstr[2] = '\0';
+                       numwidth = 0.0;
+               }
+
+               else if (*t == TSR_ADD) {
+                       if (really_print) {
+                               pr_string(x + totalwidth,
+                                       Staffs_y[staffno]
+                                       - strheight(plusstr)/2.0 + 1.5 * Stdpad,
+                                       plusstr, J_LEFT, (char *) 0, -1);
+                       }
+                       totalwidth += strwidth(plusstr);
+               }
+
+               else {
+                       /* If first denominator number, use as is,
+                        * otherwise have to add a plus sign first */
+                       if (numstr[2] != '\0') {
+                               (void) strcat(numstr, "+");
+                       }
+                       (void) sprintf(numstr + strlen(numstr), "%d", *t);
+                       numjam += tsjam(*t);
+               }
+       }
+
+       return (totalwidth);
+}
+\f
+
+/* Return the amount by which to jam the digits of a time signature number
+ * together. Could be zero (if a 1-digit number or a number that doesn't
+ * need jamming).
+ */
+
+static double
+tsjam(num)
+
+int num;
+
+{
+        /* jam numbers 10 and 13-19 together a bit */
+        return ( (num == 10 || (num > 12 && num < 20)) ? 2.0 * Stdpad : 0.0);
+}
+\f
+
+
+/* print a number that is part of a time signature. The number is passed
+ * as a string in str, and is to be printed as the given (x,y). Some 2-digit
+ * numbers look better if jammed together somewhat, so if jam is non-zero,
+ * jam them by that much, else just print the str as is.
+ */
+
+static void
+pr_tsnum(x, y, str, jam)
+
+double x;
+double y;
+char *str;
+double jam;
+
+{
+       char save;
+
+       if (jam > 0.0) {
+               /* split and print 1 digit at a time */
+               save = str[3];
+               str[3] = '\0';
+               pr_string(x, y, str, J_LEFT, (char *) 0, -1);
+               str[2] = save;
+               pr_string(_Cur[AX] - jam, y, str, J_LEFT, (char *) 0, -1);
+       }
+       else {
+               pr_string(x, y, str, J_LEFT, (char *) 0, -1);
+       }
+}
+\f
+
+/* print a string */
+
+void
+pr_string(x, y, string, justify, fname, lineno)
+
+double x, y;   /* where to put it */
+char *string;  /* what to print */
+int justify;   /* J_LEFT, etc */
+char *fname;   /* file name for error messages */
+int lineno;    /* line number for error messages */
+
+{
+       /* This function is now just a wrapper that passes its arguments
+        * pass to a more general function. The added -1.0 argument says
+        * to not spread out for right justified paragraph. */
+       pr_wstring(x, y, string, justify, -1.0, fname, lineno);
+}
+
+/* more general string printing function that handles right justified paragraphs */
+
+static void
+pr_wstring(x, y, string, justify, fullwidth, fname, lineno)
+
+double x, y;   /* where to put it */
+char *string;  /* what to print */
+int justify;   /* J_LEFT, etc */
+double fullwidth;      /* width to use, or negative value to use strwidth */
+char *fname;   /* file name for error messages */
+int lineno;    /* line number for error messages */
+
+{
+       /* skip any empty strings */
+       if ( ( string == (char *) 0) || (*string == '\0') ) {
+               return;
+       }
+
+       /* set font and size */
+       pr_font( (int) string[0], (int) string[1]);
+
+       if (IS_BOXED(string) == YES) {
+               /* The strheight and width already include the box dimension,
+                * so print the box of that size. Then adjust the x of
+                * the string so it will print at the right place
+                * inside the box. */
+               pr_box(x + 1.5 * STDPAD, y - strdescent(string) + 3.0 * STDPAD,
+                               strheight(string) - 5.0 * STDPAD,
+                               strwidth(string) - (1.5 * STDPAD));
+
+
+               x += 3.5 * STDPAD;
+       }
+       if (IS_CIRCLED(string) == YES) {
+               float circ_height;
+               float circ_width;
+               float elongation_factor;
+               float x_offset;
+               float radius;
+               float x_center, y_center;
+
+               /* determine where to place the circle and its contents */
+               (void) circled_dimensions(string, &circ_height, &circ_width,
+                                               (float *) 0, &x_offset);
+               x_center = x + circ_width / 2.0;
+               y_center = y + strascent(string) - strheight(string) / 2.0;
+
+               /* we will fiddle with the transform matrix so do inside
+                * save/restore */
+               outop(O_GSAVE);
+               outop(O_NEWPATH);
+
+               /* draw the outer elipse */
+               elongation_factor = circ_width / circ_height;
+               radius = strheight(string) / 2.0;
+               do_scale(elongation_factor, 1.0);
+               draw_circle(x_center / elongation_factor, y_center, radius);
+
+               /* undo the outer elongation, and set for inner */
+               do_scale(1.0 / elongation_factor, 1.0);
+               elongation_factor = (circ_width - 1.5 * Stdpad)
+                                       / (circ_height - 1.5 * Stdpad);
+               do_scale(elongation_factor, 1.0);
+
+               /* the inner circle's radius is smaller than outer */
+               radius = radius - 0.5 * Stdpad;
+               draw_circle(x_center / elongation_factor, y_center, radius);
+
+               /* fill in the area between the inner and outer elipses */
+               outop(O_EOFILL);
+               outop(O_GRESTORE);
+
+               /* adjust x for where text should be printed */
+               x += x_offset;
+       }
+
+       split_a_string(x, y, string, justify, fullwidth, fname, lineno);
+}
+\f
+
+/* Draw a circle (or maybe elipse, if scaling is in effect) */
+
+static void
+draw_circle(x, y, radius)
+
+double x, y;   /* of circle center */
+double radius;
+
+{
+       outcoord(x);
+       outcoord(y);
+       outcoord(radius);
+       outint(0);
+       outint(360);
+       outop(O_ARC);
+}
+\f
+
+/* output instructions for setting font and size */
+
+static void
+pr_font(font, size)
+
+int font;
+int size;
+
+{
+#ifdef SMALLMEMORY
+       /* if memory is scarce, every time we do a new font,
+        * do it in a separate save context */
+       if (Did_save == YES) {
+               outop(O_RESTORE);
+       }
+       outop(O_SAVE);
+       Did_save = YES;
+#endif
+
+       Curr_font = font;
+       Curr_size = size;
+
+       prfontname(font);
+
+       outop(O_FONT);
+
+       outint(size);
+       outop(O_SIZE);
+       outop(O_SETFONT);
+
+       Font_used[font] = YES;
+}
+\f
+
+/* print font name */
+
+static void
+prfontname(font)
+
+int font;
+{
+       OUTP(("/%s ", Fontinfo[font_index(font)].ps_name));
+}
+\f
+
+/* split a string into lines and print each line */
+
+static void
+split_a_string(x, y, string, justify, fullwidth, fname, lineno)
+
+double x;              /* coordinate at which to print string */
+double y;
+char *string;          /* what string to print */
+int justify;           /* J_LEFT, etc */
+double fullwidth;      /* width of (possibly multi-line) string, or -1.0
+                        * if the string width should be used. */
+char *fname;           /* file name for error messages */
+int lineno;            /* line number for error messages */
+
+{
+       int font, size;         /* current font and size */
+       int origfont, origsize; /* font & size at beginning of current line
+                                * of text */
+       char *text;             /* beginning of text of current line */
+       char *p;                /* pointer to current place in string */
+       int c;                  /* character read from string */
+       char *buff;             /* temporary copy of one line of string */
+
+
+       origfont = font = string[0];
+       origsize = size = string[1];
+       text = string + 2;
+
+       /* if centering or right justifying, will need width of entire
+        * (possibly multi-line) string, to adjust lines within the string */
+       if (fullwidth < 0.0) {
+               fullwidth = strwidth(string);
+       }
+
+       if (IS_BOXED(string) == YES) {
+               /* The box printing is dealt with in pr_string(), so we
+                * can ignore the BOX commands here (and need to, in order
+                * to make things align properly). */
+               text++;
+               fullwidth -= 7.0 * STDPAD;
+       }
+       p = text;
+       MALLOCA(char, buff, strlen(string) + 1);
+       do {
+               c = next_str_char(&p, &font, &size);
+               if (c == '\n' || c == '\0') {
+                       /* end of line. Print this line. Put into
+                        * temporary buffer in case more than one line */
+                       buff[0] = (char) origfont;
+                       buff[1] = (char) origsize;
+                       (void) memcpy(buff + 2, text, (unsigned) (p - text));
+                       buff[p - text + 2] = '\0';
+                       /* On final line of a justified paragraph, we don't
+                        * want to stretch that line out, because it might
+                        * only contain a couple words. */
+                       if (justify == J_JUSTPARA && c == '\0') {
+                               justify = J_LEFT;
+                       }
+
+                       j_outstring(x, y, buff, justify, fullwidth,
+                                               fname, lineno);
+
+                       /* prepare for next line, if any */
+                       origfont = font;
+                       origsize = size;
+                       text = p;
+                       y -= fontheight(font, size);
+               }
+       } while (c != '\0');
+       FREE(buff);
+}
+\f
+
+/* output a string segment with specified justification. If J_LEFT, just
+ * print given string at given x, y location. If J_CENTER, put half way
+ * between x and (x + fullwidth). If J_RIGHT, print such that right edge
+ * of string will be at (x + fullwidth) */
+
+static void
+j_outstring(x, y, string, justify, fullwidth, fname, lineno)
+
+double x;
+double y;
+char *string;          /* which string to print */
+int justify;           /* J_LEFT, etc */
+double fullwidth;      /* full width to allocate to string */
+char *fname;           /* file name for error messages */
+int lineno;            /* line number for error messages */
+
+{
+       switch (justify) {
+       case J_NONE:
+               /* NONE is effectively the same as LEFT */
+               /*FALLTHRU*/
+       case J_LEFT:
+       case J_RAGPARA:
+               outstring(x, y, -1.0, string, fname, lineno);
+               break;
+       case J_JUSTPARA:
+               outstring(x, y, fullwidth, string, fname, lineno);
+               break;
+       case J_CENTER:
+               outstring(x + (fullwidth - strwidth(string)) / 2.0, y,
+                               -1.0, string, fname, lineno);
+               break;
+       case J_RIGHT:
+               outstring(x + fullwidth - strwidth(string), y, -1.0,
+                               string, fname, lineno);
+               break;
+       default:
+               pfatal("bad justification type");
+               /*NOTREACHED*/
+               break;
+       }
+}
+\f
+
+/* given a MAINLL struct, find all the STAFF structs from there to the next
+ * BAR, and fill in a table of the staff Y coordinates */
+
+static void
+set_staff_y(main_p)
+
+struct MAINLL *main_p;
+
+{
+       int s;
+
+       /* First initialize all to 0.0. This is so that if the Staffs_y
+        * array is accessed for a non-existent staff, we will be sure
+        * that it will be set to 0.0, which is the special value to mean
+        * non-existent. Otherwise, when the number of staffs decreases,
+        * an old value could get left around in the staff that went away. */
+       for (s = 1; s <= MAXSTAFFS; s++) {
+               Staffs_y[s] = 0.0;
+       }
+
+       for (   ; main_p != (struct MAINLL *) 0; main_p = main_p->next) {
+
+               if (main_p->str == S_BAR) {
+                       /* reached end of list of staffs in this measure */
+                       return;
+               }
+
+               if (main_p->str == S_STAFF) {
+                       /* save y value of staff */
+                       Staffs_y[main_p->u.staff_p->staffno] = 
+                                       main_p->u.staff_p->c[AY];
+               }
+       }
+}
+\f
+
+/* print measure number at beginning of score if user wants them */
+
+static void
+pr_meas_num(staffno, x)
+
+int staffno;   /* which staff to possible put measure number on */
+double x;      /* where to put measure number */
+
+{
+       float y_adj;    /* to avoid clefs */
+       int clef;
+
+
+       /* measure numbers only put on those staffs that have endings */
+       if (has_ending(staffno) ) {
+
+               /* print measure number if user wants them */
+               if ( (svpath(staffno, MEASNUM)->measnum == YES)
+                                               && (Meas_num > 1)) {
+
+                       /* construct the measure number string */
+                       char mnumstr[8];
+                       mnumstr[0] = (char) (Score.measnumfamily
+                                                       + Score.measnumfont);
+                       mnumstr[1] = (char) Score.measnumsize;
+                       (void) sprintf(mnumstr + 2, "%d", Meas_num);
+
+                       /* print it */
+                       if (is_tab_staff(staffno) == YES) {
+                               clef = TABCLEF;
+                       }
+                       /* If clef is not to be printed, use NOCLEF.
+                        * (printclef shares the STAFFLINES used flag) */
+                       else if (svpath(staffno, STAFFLINES)->printclef == NO) {
+                               clef = NOCLEF;
+                       }
+                       else {
+                               clef = svpath(staffno, CLEF)->clef;
+                       }
+                       /* Figure out where to place the measure number
+                        * vertically by calling clefspace to get
+                        * the height of the clef on the current staff plus
+                        * the height of the measure number, but ignoring
+                        * the height of the clef above (if any), then
+                        * subtract the ascent of the measure number to
+                        * get the right baseline. */
+                       y_adj = halfstaffhi(staffno) +
+                               clefspace(NOCLEF, 1.0, clef, Staffscale, YES) -
+                               fontascent((int) mnumstr[0], (int) mnumstr[1]);
+                       pr_string(x + 1.5 * Stepsize,
+                                       Staffs_y[staffno] + y_adj,
+                                       mnumstr, J_LEFT, (char *) 0, -1);
+               }
+       }
+}
+\f
+
+/* tell PostScript about file and linenumber */
+
+void
+pr_linenum (inputfile, inputlineno)
+
+char *inputfile;
+int inputlineno;
+
+{
+       static char *fname = "";                /* keep track of current file
+                                                * name to only output it when
+                                                * it changes */
+       char *str;                              /* walk thru file name to
+                                                * add backslashes if needed */
+
+       
+       if (strcmp(fname, inputfile) != 0) {
+               OUTPCH(('('));
+               for (str = inputfile; *str != 0; str++) {
+                       switch(*str) {
+                       case '\\':
+                       case '(':
+                       case ')':
+                               OUTPCH(('\\'));
+                               /*FALLTHRU*/
+                       default:
+                               OUTPCH((*str));
+                               break;
+                       }
+               }
+               OUTP((") inputfile\n"));
+               fname = inputfile;
+       }
+       OUTP(("%d linenum\n", inputlineno));
+}
+\f
+
+/* output the current scale factor */
+
+static void
+setscale()
+
+{
+       OUTP(("%f %f scale\n", Score.scale_factor, Score.scale_factor));
+}
+\f
+
+/* For debugging, this generates PostScript to draw colored bounding boxes
+ * representing coordinates (the 13-element arrays called "c"
+ * in various structs).
+ */
+
+static void
+show_coord(coord_p, index)
+
+float *coord_p;                /* which one to draw */
+int index;             /* index into Bbox_list, to get colors to use */
+
+{
+       struct Bbox *bb_p;
+
+       bb_p = &(Bbox_list[index]);
+       outop(O_GSAVE);
+       OUTP(("%d.%d %d.%d %d.%d setrgbcolor\n",
+               bb_p->red / 100, bb_p->red % 100,
+               bb_p->green / 100, bb_p->green % 100,
+               bb_p->blue / 100, bb_p->blue % 100));
+       if (bb_p->dash_on && bb_p->dash_off) {
+               OUTP(("[%d %d] 0 setdash\n", bb_p->dash_on, bb_p->dash_off));
+       }
+       pr_box(coord_p[AW], coord_p[AS], coord_p[AN] - coord_p[AS], coord_p[AE] - coord_p[AW]);
+       outop(O_GRESTORE);
+}
+\f
+
+/* The environment variable MUP_BB turns on drawing of bounding boxes around
+ * things, for debugging.  This function checks if the variable is set,
+ * and if so, parses it to see which subset of things to draw boxes for.
+ * For example, MUP_BB=g just does grpsyls, whereas MUP_BB=gnc does grpsyls,
+ * notes, and chords. Bbox_list gives the full list of possibilities.
+ */
+
+static void
+prep_bbox()
+{
+       char *bb;
+       int i;
+
+       if ((bb = getenv("MUP_BB")) == 0) {
+               /* user doesn't want bounding box debugging */
+               return;
+       }
+
+       /* If a coordinate type's id is set in MUP_BB, set its corresponding
+        * flag bit */
+       for (i = 0; i < NUMELEM(Bbox_list); i++) {
+               if (strchr(bb, Bbox_list[i].id) != 0) {
+                       BB_SET(i);
+               }
+       }
+}
+\f
+
+/* To help with debugging the placement phase of Mup,
+ * or just to help a user see why things are laid out as they are,
+ * this function will draw colored bounding boxes around things that
+ * have "coordinates." The environment variable MUP_BB control which,
+ * if any, kinds of things this is done for. This function is given the
+ * main list item at the end of a page. It backs up through the list, back to
+ * the beginning of the page, generating PostScript code to cause
+ * printing of relevant boxes. Doing this last on a page ensures the boxes
+ * are drawn on top of other things, and is at least as easy as going forwards.
+ */
+
+static void
+show_bounding_boxes(mll_p)
+
+struct MAINLL *mll_p;  /* FEED for end of current page, or could be
+                        * Mainlltc_p if at end of song. */
+
+{
+       int v;                  /* voice/verse */
+       int n;                  /* NOTE index */
+       struct GRPSYL *gs_p;
+       struct CHORD *chord_p;
+       struct STUFF *stuff_p;
+
+       /* We are at bottom of main list for current page. Work upwards,
+        * printing any coords we find. */
+       if (mll_p->str == S_FEED) {
+               /* Skip this feed.
+                * We want to back up to previous page feed, if any */
+               mll_p = mll_p->prev;
+       }
+
+       for (   ; mll_p != 0; mll_p = mll_p->prev) {
+               switch (mll_p->str) {
+
+               case S_BAR:
+                       if (BB_IS_SET(BB_BAR)) {
+                               show_coord(mll_p->u.bar_p->c, BB_BAR);
+                       }
+                       break;
+
+               case S_FEED:
+                       if (BB_IS_SET(BB_FEED)) {
+                               show_coord(mll_p->u.feed_p->c, BB_FEED);
+                       }
+                       if (mll_p->u.feed_p->pagefeed == YES
+                                                       || Feednumber == 1) {
+                               if (BB_IS_SET(BB_BLOCKHEAD)) {
+                                       /* Do header/footer */
+                                       if (Feednumber == 1) {
+                                               show_coord(Header.c, BB_BLOCKHEAD);
+                                               show_coord(Footer.c, BB_BLOCKHEAD);
+                                       }
+                                       else {
+                                               show_coord(Header2.c, BB_BLOCKHEAD);
+                                               show_coord(Footer2.c, BB_BLOCKHEAD);
+                                       }
+                                       if (mll_p->u.feed_p->top_p != 0){
+                                               set_win_coord(mll_p->u.feed_p->top_p->c);
+                                               show_coord(mll_p->u.feed_p->top_p->c, BB_BLOCKHEAD);
+                                               set_win_coord(0);
+                                       }
+                                       if (mll_p->u.feed_p->bot_p != 0){
+                                               set_win_coord(mll_p->u.feed_p->bot_p->c);
+                                               show_coord(mll_p->u.feed_p->bot_p->c, BB_BLOCKHEAD);
+                                               set_win_coord(0);
+                                       }
+                               }
+                               if (mll_p->u.feed_p->pagefeed == YES) {
+                                       /* reached top of current page; we're done */
+                                       return;
+                               }
+                       }
+                       break;
+
+               case S_STAFF:
+                       if (mll_p->u.staff_p->visible == NO) {
+                               break;
+                       }
+
+                       /* show the staff itself */
+                       if (BB_IS_SET(BB_STAFF)) {
+                               show_coord(mll_p->u.staff_p->c, BB_STAFF);
+                       }
+
+                       /* Do groups and notes */
+                       if (BB_IS_SET(BB_GRPSYL) || BB_IS_SET(BB_NOTE)) {
+                               for (v = 0; v < MAXVOICES; v++) {
+
+                                       if (vvpath(mll_p->u.staff_p->staffno,
+                                                       v+1, VISIBLE)->visible
+                                                       == NO) {
+                                               /* Skip invisible voices */
+                                               continue;
+                                       }
+
+                                       for (gs_p = mll_p->u.staff_p->groups_p[v];
+                                                       gs_p != 0;
+                                                       gs_p = gs_p->next) {
+                                               if (BB_IS_SET(BB_GRPSYL)) {
+                                                       show_coord(gs_p->c, BB_GRPSYL);
+                                               }
+                                               if (gs_p->nnotes > 0 &&
+                                                               BB_IS_SET(BB_NOTE)) {
+                                                       for (n = 0; n < gs_p->nnotes; n++) {
+                                                               show_coord(gs_p->notelist[n].c, BB_NOTE);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       /* now do lyrics */
+                       if (BB_IS_SET(BB_GRPSYL)) {
+                               for (n = 0; n < mll_p->u.staff_p->nsyllists; n++) {
+                                       for  (gs_p = mll_p->u.staff_p->syls_p[n];
+                                                       gs_p != 0;
+                                                       gs_p = gs_p->next) {
+                                               show_coord(gs_p->c, BB_GRPSYL);
+                                       }
+                               }
+                       }
+
+                       /* do the other "stuff" */
+                       if (BB_IS_SET(BB_STUFF)) {
+                               for (stuff_p = mll_p->u.staff_p->stuff_p;
+                                               stuff_p != 0;
+                                               stuff_p = stuff_p->next) {
+                                       show_coord(stuff_p->c, BB_STUFF);
+                               }
+                       }
+                       break;
+
+               case S_CHHEAD:
+                       if (BB_IS_SET(BB_CHORD)) {
+                               for (chord_p = mll_p->u.chhead_p->ch_p; chord_p != 0;
+                                               chord_p = chord_p->ch_p) {
+                                       show_coord(chord_p->c, BB_CHORD);
+                               }
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}